@instructure/canvas-rce 5.15.6 → 6.0.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 +29 -0
- package/Dockerfile +1 -1
- package/es/bridge/Bridge.d.ts +6 -0
- package/es/defaultTinymceConfig.js +165 -4
- package/es/enhance-user-content/enhance_user_content.js +1 -1
- package/es/enhance-user-content/instructure_helper.js +7 -3
- package/es/rce/RCEWrapper.js +3 -0
- package/es/rce/contentRendering.js +3 -2
- package/es/rce/plugins/instructure_image/ImageOptionsTray/index.js +33 -9
- package/es/rce/plugins/instructure_rce_external_tools/RceToolWrapper.d.ts +1 -1
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.d.ts +13 -3
- package/es/rce/plugins/instructure_record/VideoOptionsTray/TrayController.js +10 -0
- package/es/rce/plugins/instructure_record/mediaTranslations.d.ts +3 -0
- package/es/rce/plugins/instructure_record/mediaTranslations.js +4 -1
- package/es/rce/plugins/shared/DimensionsInput/DimensionInput.d.ts +4 -2
- package/es/rce/plugins/shared/DimensionsInput/DimensionInput.js +10 -3
- package/es/rce/plugins/shared/DimensionsInput/index.d.ts +2 -0
- package/es/rce/plugins/shared/DimensionsInput/index.js +9 -5
- package/es/rce/plugins/shared/ImageOptionsForm.d.ts +4 -1
- package/es/rce/plugins/shared/ImageOptionsForm.js +13 -3
- package/es/rce/plugins/shared/Upload/UrlPanel.d.ts +9 -3
- package/es/rce/plugins/shared/Upload/UrlPanel.js +13 -4
- package/es/rce/plugins/shared/do-fetch-api-effect/parse-link-header.js +1 -1
- package/es/rce/plugins/tinymce-a11y-checker/node-checker.js +3 -2
- package/es/rce/plugins/tinymce-a11y-checker/utils/dom.d.ts +6 -0
- package/es/rce/plugins/tinymce-a11y-checker/utils/dom.js +15 -0
- package/es/rce/plugins/tinymce-a11y-checker/utils/rule-enhancer.d.ts +14 -0
- package/es/rce/plugins/tinymce-a11y-checker/utils/rule-enhancer.js +53 -0
- package/es/rce/screenreaderOnFormat.d.ts +2 -0
- package/es/rce/screenreaderOnFormat.js +109 -0
- package/es/translations/locales/ar.js +42 -0
- package/es/translations/locales/ca.js +42 -0
- package/es/translations/locales/cy.js +42 -0
- package/es/translations/locales/da-x-k12.js +42 -0
- package/es/translations/locales/da.js +42 -0
- package/es/translations/locales/de.js +42 -0
- package/es/translations/locales/en-AU-x-unimelb.js +42 -0
- package/es/translations/locales/en-GB-x-ukhe.js +42 -0
- package/es/translations/locales/en.js +51 -0
- package/es/translations/locales/en_AU.js +42 -0
- package/es/translations/locales/en_CA.js +42 -0
- package/es/translations/locales/en_CY.js +42 -0
- package/es/translations/locales/en_GB.js +42 -0
- package/es/translations/locales/es.js +42 -0
- package/es/translations/locales/es_ES.js +42 -0
- package/es/translations/locales/fi.js +42 -0
- package/es/translations/locales/fr.js +42 -0
- package/es/translations/locales/fr_CA.js +42 -0
- package/es/translations/locales/ga.js +114 -0
- package/es/translations/locales/hi.js +42 -0
- package/es/translations/locales/ht.js +42 -0
- package/es/translations/locales/id.js +42 -0
- package/es/translations/locales/is.js +42 -0
- package/es/translations/locales/it.js +42 -0
- package/es/translations/locales/ja.js +42 -0
- package/es/translations/locales/mi.js +42 -0
- package/es/translations/locales/ms.js +42 -0
- package/es/translations/locales/nb-x-k12.js +42 -0
- package/es/translations/locales/nb.js +42 -0
- package/es/translations/locales/nl.js +42 -0
- package/es/translations/locales/pl.js +42 -0
- package/es/translations/locales/pt.js +42 -0
- package/es/translations/locales/pt_BR.js +42 -0
- package/es/translations/locales/ru.js +42 -0
- package/es/translations/locales/sl.js +42 -0
- package/es/translations/locales/sv-x-k12.js +42 -0
- package/es/translations/locales/sv.js +42 -0
- package/es/translations/locales/th.js +42 -0
- package/es/translations/locales/vi.js +42 -0
- package/es/translations/locales/zh-Hans.js +42 -0
- package/es/translations/locales/zh-Hant.js +42 -0
- package/es/translations/locales/zh.js +42 -0
- package/es/translations/locales/zh_HK.js +42 -0
- package/es/util/loadingPlaceholder.js +4 -3
- package/package.json +6 -5
- package/coverage/canvas-rce-jest.xml +0 -7028
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import React from 'react';
|
|
20
|
-
import { bool, func, number, shape, string } from 'prop-types';
|
|
20
|
+
import { bool, func, number, shape, string, object } from 'prop-types';
|
|
21
21
|
import { ScreenReaderContent } from '@instructure/ui-a11y-content';
|
|
22
22
|
import { FormFieldGroup } from '@instructure/ui-form-field';
|
|
23
23
|
import { IconLockLine, IconWarningSolid } from '@instructure/ui-icons';
|
|
@@ -79,7 +79,8 @@ export default function DimensionsInput(props) {
|
|
|
79
79
|
minHeight,
|
|
80
80
|
minWidth,
|
|
81
81
|
minPercentage,
|
|
82
|
-
hidePercentage
|
|
82
|
+
hidePercentage,
|
|
83
|
+
dimensionsRef
|
|
83
84
|
} = props;
|
|
84
85
|
const handleDimensionTypeChange = e => {
|
|
85
86
|
dimensionsState.setUsePercentageUnits(e.target.value === 'percentage');
|
|
@@ -131,7 +132,8 @@ export default function DimensionsInput(props) {
|
|
|
131
132
|
}, /*#__PURE__*/React.createElement(DimensionInput, {
|
|
132
133
|
dimensionState: dimensionsState.percentageState,
|
|
133
134
|
label: formatMessage('Percentage'),
|
|
134
|
-
messages: [secondaryMessage]
|
|
135
|
+
messages: [secondaryMessage],
|
|
136
|
+
dimensionsRef: dimensionsRef
|
|
135
137
|
})), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
136
138
|
padding: "x-small small"
|
|
137
139
|
}, "%")) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Flex.Item, {
|
|
@@ -140,7 +142,8 @@ export default function DimensionsInput(props) {
|
|
|
140
142
|
dimensionState: dimensionsState.widthState,
|
|
141
143
|
label: formatMessage('Width'),
|
|
142
144
|
minValue: minWidth,
|
|
143
|
-
messages: [secondaryMessage]
|
|
145
|
+
messages: [secondaryMessage],
|
|
146
|
+
dimensionsRef: dimensionsRef
|
|
144
147
|
})), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
145
148
|
padding: "x-small small"
|
|
146
149
|
}, /*#__PURE__*/React.createElement(IconLockLine, null)), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
@@ -176,7 +179,8 @@ DimensionsInput.propTypes = {
|
|
|
176
179
|
minHeight: number.isRequired,
|
|
177
180
|
minWidth: number.isRequired,
|
|
178
181
|
minPercentage: number.isRequired,
|
|
179
|
-
hidePercentage: bool
|
|
182
|
+
hidePercentage: bool,
|
|
183
|
+
dimensionsRef: object
|
|
180
184
|
};
|
|
181
185
|
DimensionsInput.defaultProps = {
|
|
182
186
|
hidePercentage: false
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export default ImageOptionsForm;
|
|
2
|
-
declare function ImageOptionsForm({ imageSize, displayAs, isDecorativeImage, altText, isLinked, dimensionsState, handleAltTextChange, handleIsDecorativeChange, handleDisplayAsChange, handleImageSizeChange, messagesForSize, hideDimensions, id, isIconMaker, forBlockEditorUse, }: {
|
|
2
|
+
declare function ImageOptionsForm({ imageSize, displayAs, isDecorativeImage, altText, isLinked, dimensionsState, handleAltTextChange, handleIsDecorativeChange, handleDisplayAsChange, handleImageSizeChange, messagesForSize, hideDimensions, id, isIconMaker, forBlockEditorUse, altHasError, altRef, dimensionsRef }: {
|
|
3
3
|
imageSize: any;
|
|
4
4
|
displayAs: any;
|
|
5
5
|
isDecorativeImage: any;
|
|
@@ -15,5 +15,8 @@ declare function ImageOptionsForm({ imageSize, displayAs, isDecorativeImage, alt
|
|
|
15
15
|
id?: string | undefined;
|
|
16
16
|
isIconMaker?: boolean | undefined;
|
|
17
17
|
forBlockEditorUse?: boolean | undefined;
|
|
18
|
+
altHasError?: boolean | undefined;
|
|
19
|
+
altRef?: null | undefined;
|
|
20
|
+
dimensionsRef?: null | undefined;
|
|
18
21
|
}): React.JSX.Element;
|
|
19
22
|
import React from 'react';
|
|
@@ -44,7 +44,10 @@ const ImageOptionsForm = ({
|
|
|
44
44
|
hideDimensions,
|
|
45
45
|
id = 'image-options-form',
|
|
46
46
|
isIconMaker = false,
|
|
47
|
-
forBlockEditorUse = false
|
|
47
|
+
forBlockEditorUse = false,
|
|
48
|
+
altHasError = false,
|
|
49
|
+
altRef = null,
|
|
50
|
+
dimensionsRef = null
|
|
48
51
|
}) => {
|
|
49
52
|
const TYPE = isIconMaker ? formatMessage('icon') : formatMessage('image');
|
|
50
53
|
const tooltipText = formatMessage('Used by screen readers to describe the content of an {TYPE}', {
|
|
@@ -76,6 +79,7 @@ const ImageOptionsForm = ({
|
|
|
76
79
|
}, /*#__PURE__*/React.createElement(Flex.Item, {
|
|
77
80
|
padding: "small"
|
|
78
81
|
}, /*#__PURE__*/React.createElement(TextArea, {
|
|
82
|
+
"data-testid": "alt-text-field",
|
|
79
83
|
disabled: isDecorativeImage,
|
|
80
84
|
"aria-describedby": "alt-text-label-tooltip",
|
|
81
85
|
height: "4rem",
|
|
@@ -85,7 +89,12 @@ const ImageOptionsForm = ({
|
|
|
85
89
|
TYPE
|
|
86
90
|
}),
|
|
87
91
|
resize: "vertical",
|
|
88
|
-
value: altText
|
|
92
|
+
value: altText,
|
|
93
|
+
messages: altHasError ? [{
|
|
94
|
+
text: formatMessage('Invalid description'),
|
|
95
|
+
type: 'error'
|
|
96
|
+
}] : [],
|
|
97
|
+
ref: altRef
|
|
89
98
|
})), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
90
99
|
padding: "small"
|
|
91
100
|
}, /*#__PURE__*/React.createElement(Checkbox, {
|
|
@@ -133,7 +142,8 @@ const ImageOptionsForm = ({
|
|
|
133
142
|
disabled: displayAs !== 'embed',
|
|
134
143
|
minHeight: MIN_HEIGHT,
|
|
135
144
|
minWidth: MIN_WIDTH,
|
|
136
|
-
minPercentage: MIN_PERCENTAGE
|
|
145
|
+
minPercentage: MIN_PERCENTAGE,
|
|
146
|
+
dimensionsRef: dimensionsRef
|
|
137
147
|
})))));
|
|
138
148
|
};
|
|
139
149
|
export default ImageOptionsForm;
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
declare function UrlPanel({ fileUrl, setFileUrl }: {
|
|
1
|
+
declare function UrlPanel({ fileUrl, setFileUrl, urlHasError, urlRef }: {
|
|
2
2
|
fileUrl: any;
|
|
3
3
|
setFileUrl: any;
|
|
4
|
+
urlHasError: any;
|
|
5
|
+
urlRef: any;
|
|
4
6
|
}): React.JSX.Element;
|
|
5
7
|
declare namespace UrlPanel {
|
|
6
8
|
namespace propTypes {
|
|
7
|
-
let fileUrl: import("prop-types").Validator<string>;
|
|
8
|
-
let setFileUrl: import("prop-types").Validator<(...args: any[]) => any>;
|
|
9
|
+
export let fileUrl: import("prop-types").Validator<string>;
|
|
10
|
+
export let setFileUrl: import("prop-types").Validator<(...args: any[]) => any>;
|
|
11
|
+
export { bool as urlHasError };
|
|
12
|
+
export { object as urlRef };
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
15
|
export default UrlPanel;
|
|
12
16
|
import React from 'react';
|
|
17
|
+
import { bool } from 'prop-types';
|
|
18
|
+
import { object } from 'prop-types';
|
|
@@ -17,22 +17,31 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import React from 'react';
|
|
20
|
-
import { string, func } from 'prop-types';
|
|
20
|
+
import { string, func, bool, object } from 'prop-types';
|
|
21
21
|
import { TextInput } from '@instructure/ui-text-input';
|
|
22
22
|
import formatMessage from '../../../../format-message';
|
|
23
23
|
export default function UrlPanel({
|
|
24
24
|
fileUrl,
|
|
25
|
-
setFileUrl
|
|
25
|
+
setFileUrl,
|
|
26
|
+
urlHasError,
|
|
27
|
+
urlRef
|
|
26
28
|
}) {
|
|
27
29
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(TextInput, {
|
|
28
30
|
name: "file-url",
|
|
29
31
|
renderLabel: formatMessage('File URL'),
|
|
30
32
|
type: "url",
|
|
31
33
|
value: fileUrl,
|
|
32
|
-
onChange: (
|
|
34
|
+
onChange: (_e, val) => setFileUrl(val),
|
|
35
|
+
messages: urlHasError ? [{
|
|
36
|
+
text: formatMessage('Invalid URL'),
|
|
37
|
+
type: 'error'
|
|
38
|
+
}] : [],
|
|
39
|
+
ref: urlRef
|
|
33
40
|
}));
|
|
34
41
|
}
|
|
35
42
|
UrlPanel.propTypes = {
|
|
36
43
|
fileUrl: string.isRequired,
|
|
37
|
-
setFileUrl: func.isRequired
|
|
44
|
+
setFileUrl: func.isRequired,
|
|
45
|
+
urlHasError: bool,
|
|
46
|
+
urlRef: object
|
|
38
47
|
};
|
|
@@ -83,7 +83,7 @@ function intoRels(acc, x) {
|
|
|
83
83
|
});
|
|
84
84
|
return acc;
|
|
85
85
|
}
|
|
86
|
-
const PARSE_LINK_HEADER_MAXLEN =
|
|
86
|
+
const PARSE_LINK_HEADER_MAXLEN = 4000;
|
|
87
87
|
const PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED = process.env.PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED != null;
|
|
88
88
|
function checkHeader(linkHeader) {
|
|
89
89
|
if (!linkHeader) return false;
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import * as dom from './utils/dom';
|
|
20
20
|
import rules from './rules';
|
|
21
|
+
import { enhanceRules } from './utils/rule-enhancer';
|
|
21
22
|
export default function checkNode(node, done, config = {}, additionalRules = []) {
|
|
22
23
|
if (!node) {
|
|
23
24
|
return;
|
|
@@ -25,9 +26,9 @@ export default function checkNode(node, done, config = {}, additionalRules = [])
|
|
|
25
26
|
const errors = [];
|
|
26
27
|
const childNodeCheck = child => {
|
|
27
28
|
if (child.hasAttribute('data-ignore-a11y-check')) return;
|
|
28
|
-
|
|
29
|
+
// Enhance all rules with TinyMCE notification functionality
|
|
30
|
+
const composedRules = enhanceRules(rules.concat(additionalRules));
|
|
29
31
|
for (const rule of composedRules) {
|
|
30
|
-
// eslint-disable-next-line promise/catch-or-return
|
|
31
32
|
Promise.resolve(rule.test(child, config)).then(result => {
|
|
32
33
|
if (!result) {
|
|
33
34
|
errors.push({
|
|
@@ -8,4 +8,10 @@ export function onlyContainsLink(elem: any): boolean;
|
|
|
8
8
|
export function splitStyleAttribute(styleString: any): any;
|
|
9
9
|
export function createStyleString(styleObj: any): string;
|
|
10
10
|
export function hasTextNode(elem: any): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Notifies TinyMCE that a change has been made
|
|
13
|
+
* This ensures that changes persist in the editor's state without requiring additional user actions
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
export function notifyTinyMCE(): void;
|
|
11
17
|
import indicate from './indicate';
|
|
@@ -130,4 +130,19 @@ export function createStyleString(styleObj) {
|
|
|
130
130
|
export function hasTextNode(elem) {
|
|
131
131
|
const nodes = Array.from(elem.childNodes);
|
|
132
132
|
return nodes.some(x => x.nodeType === Node.TEXT_NODE);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Notifies TinyMCE that a change has been made
|
|
137
|
+
* This ensures that changes persist in the editor's state without requiring additional user actions
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
export function notifyTinyMCE() {
|
|
141
|
+
// Get the active TinyMCE editor instance
|
|
142
|
+
const editor = window.tinymce?.activeEditor;
|
|
143
|
+
if (editor) {
|
|
144
|
+
// Mark the content as dirty to ensure TinyMCE knows it has changed
|
|
145
|
+
editor.undoManager.add();
|
|
146
|
+
editor.fire('Change');
|
|
147
|
+
}
|
|
133
148
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhances a rule by wrapping its update method to notify TinyMCE of changes
|
|
3
|
+
* @param {Object} rule - The rule to enhance
|
|
4
|
+
* @param {Function} [enhanceMethod] - Optional custom method to notify TinyMCE
|
|
5
|
+
* @returns {Object} - The enhanced rule
|
|
6
|
+
*/
|
|
7
|
+
export function enhanceRule(rule: Object, enhanceMethod?: Function): Object;
|
|
8
|
+
/**
|
|
9
|
+
* Enhances an array of rules by wrapping their update methods to notify TinyMCE of changes
|
|
10
|
+
* @param {Array} rules - The array of rules to enhance
|
|
11
|
+
* @param {Function} [enhanceMethod] - Optional custom method to notify TinyMCE
|
|
12
|
+
* @returns {Array} - The array of enhanced rules
|
|
13
|
+
*/
|
|
14
|
+
export function enhanceRules(rules: any[], enhanceMethod?: Function): any[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2025 - 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
|
+
|
|
19
|
+
import { notifyTinyMCE } from './dom';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Enhances a rule by wrapping its update method to notify TinyMCE of changes
|
|
23
|
+
* @param {Object} rule - The rule to enhance
|
|
24
|
+
* @param {Function} [enhanceMethod] - Optional custom method to notify TinyMCE
|
|
25
|
+
* @returns {Object} - The enhanced rule
|
|
26
|
+
*/
|
|
27
|
+
export function enhanceRule(rule, enhanceMethod = null) {
|
|
28
|
+
// Skip if the rule doesn't have an update method
|
|
29
|
+
if (!rule.update) {
|
|
30
|
+
return rule;
|
|
31
|
+
}
|
|
32
|
+
const enhance = enhanceMethod || notifyTinyMCE;
|
|
33
|
+
const enhancedRule = {
|
|
34
|
+
...rule
|
|
35
|
+
};
|
|
36
|
+
const originalUpdate = rule.update;
|
|
37
|
+
enhancedRule.update = (elem, data) => {
|
|
38
|
+
const result = originalUpdate(elem, data);
|
|
39
|
+
enhance();
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
return enhancedRule;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Enhances an array of rules by wrapping their update methods to notify TinyMCE of changes
|
|
47
|
+
* @param {Array} rules - The array of rules to enhance
|
|
48
|
+
* @param {Function} [enhanceMethod] - Optional custom method to notify TinyMCE
|
|
49
|
+
* @returns {Array} - The array of enhanced rules
|
|
50
|
+
*/
|
|
51
|
+
export function enhanceRules(rules, enhanceMethod = null) {
|
|
52
|
+
return rules.map(rule => enhanceRule(rule, enhanceMethod));
|
|
53
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2025 - 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
|
+
|
|
19
|
+
import formatMessage from '../format-message';
|
|
20
|
+
const screenreaderMessageHolderId = 'rce_message_screenreader_holder';
|
|
21
|
+
const getAlertContainer = () => {
|
|
22
|
+
let alertContainer = document.getElementById(screenreaderMessageHolderId);
|
|
23
|
+
if (!alertContainer) {
|
|
24
|
+
alertContainer = document.createElement('div');
|
|
25
|
+
alertContainer.id = screenreaderMessageHolderId;
|
|
26
|
+
alertContainer.setAttribute('role', 'status');
|
|
27
|
+
alertContainer.setAttribute('aria-live', 'assertive');
|
|
28
|
+
alertContainer.setAttribute('aria-relevant', 'additions');
|
|
29
|
+
alertContainer.setAttribute('aria-atomic', 'true');
|
|
30
|
+
// copied from Canvas' .screenreader-only
|
|
31
|
+
alertContainer.setAttribute('style', 'border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; transform: translatez(0);');
|
|
32
|
+
document.body.appendChild(alertContainer);
|
|
33
|
+
}
|
|
34
|
+
return alertContainer;
|
|
35
|
+
};
|
|
36
|
+
const announce = message => {
|
|
37
|
+
const alertContainer = getAlertContainer();
|
|
38
|
+
const messageElement = document.createElement('span');
|
|
39
|
+
messageElement.textContent = message;
|
|
40
|
+
alertContainer.replaceChildren(messageElement);
|
|
41
|
+
};
|
|
42
|
+
const handleFormatApply = event => {
|
|
43
|
+
switch (event.format) {
|
|
44
|
+
case 'bold':
|
|
45
|
+
announce(formatMessage('Bold applied'));
|
|
46
|
+
break;
|
|
47
|
+
case 'italic':
|
|
48
|
+
announce(formatMessage('Italic applied'));
|
|
49
|
+
break;
|
|
50
|
+
case 'underline':
|
|
51
|
+
announce(formatMessage('Underline applied'));
|
|
52
|
+
break;
|
|
53
|
+
case 'h1':
|
|
54
|
+
case 'h2':
|
|
55
|
+
case 'h3':
|
|
56
|
+
case 'h4':
|
|
57
|
+
case 'h5':
|
|
58
|
+
case 'h6':
|
|
59
|
+
announce(formatMessage('Heading {h} applied', {
|
|
60
|
+
h: event.format
|
|
61
|
+
}));
|
|
62
|
+
break;
|
|
63
|
+
case 'p':
|
|
64
|
+
announce(formatMessage('Paragraph applied'));
|
|
65
|
+
break;
|
|
66
|
+
case 'div':
|
|
67
|
+
announce(formatMessage('Div applied'));
|
|
68
|
+
break;
|
|
69
|
+
case 'address':
|
|
70
|
+
announce(formatMessage('Address applied'));
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const handleRemoveFormat = event => {
|
|
75
|
+
switch (event.format) {
|
|
76
|
+
case 'bold':
|
|
77
|
+
announce(formatMessage('Bold removed'));
|
|
78
|
+
break;
|
|
79
|
+
case 'italic':
|
|
80
|
+
announce(formatMessage('Italic removed'));
|
|
81
|
+
break;
|
|
82
|
+
case 'underline':
|
|
83
|
+
announce(formatMessage('Underline removed'));
|
|
84
|
+
break;
|
|
85
|
+
case 'h1':
|
|
86
|
+
case 'h2':
|
|
87
|
+
case 'h3':
|
|
88
|
+
case 'h4':
|
|
89
|
+
case 'h5':
|
|
90
|
+
case 'h6':
|
|
91
|
+
announce(formatMessage('Heading {h} removed', {
|
|
92
|
+
h: event.format
|
|
93
|
+
}));
|
|
94
|
+
break;
|
|
95
|
+
case 'p':
|
|
96
|
+
announce(formatMessage('Paragraph removed'));
|
|
97
|
+
break;
|
|
98
|
+
case 'div':
|
|
99
|
+
announce(formatMessage('Div removed'));
|
|
100
|
+
break;
|
|
101
|
+
case 'address':
|
|
102
|
+
announce(formatMessage('Address removed'));
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
export const initScreenreaderOnFormat = editor => {
|
|
107
|
+
editor.on('FormatApply', handleFormatApply);
|
|
108
|
+
editor.on('FormatRemove', handleRemoveFormat);
|
|
109
|
+
};
|
|
@@ -52,6 +52,12 @@ const locale = {
|
|
|
52
52
|
"additional_considerations_f3801683": {
|
|
53
53
|
"message": "الاعتبارات الإضافية"
|
|
54
54
|
},
|
|
55
|
+
"address_applied_64269b0b": {
|
|
56
|
+
"message": "تم تطبيق العنوان"
|
|
57
|
+
},
|
|
58
|
+
"address_removed_fcb496e0": {
|
|
59
|
+
"message": "تمت إزالة العنوان"
|
|
60
|
+
},
|
|
55
61
|
"adjacent_links_with_the_same_url_should_be_a_singl_7a1f7f6c": {
|
|
56
62
|
"message": "يجب أن تكون الروابط المتجاورة بنفس عنوان URL رابطًا فرديًا."
|
|
57
63
|
},
|
|
@@ -208,6 +214,12 @@ const locale = {
|
|
|
208
214
|
"blue_daf8fea9": {
|
|
209
215
|
"message": "أزرق"
|
|
210
216
|
},
|
|
217
|
+
"bold_applied_42d547b7": {
|
|
218
|
+
"message": "تم تطبيق الخط الغامق"
|
|
219
|
+
},
|
|
220
|
+
"bold_removed_da474a5c": {
|
|
221
|
+
"message": "تمت إزالة الخط الغامق"
|
|
222
|
+
},
|
|
211
223
|
"border_5b08b06d": {
|
|
212
224
|
"message": "الحد"
|
|
213
225
|
},
|
|
@@ -574,6 +586,12 @@ const locale = {
|
|
|
574
586
|
"display_text_link_opens_in_a_new_tab_75e9afc9": {
|
|
575
587
|
"message": "عرض ارتباط النص (يفتح في علامة تبويب جديدة)"
|
|
576
588
|
},
|
|
589
|
+
"div_applied_49ad83fc": {
|
|
590
|
+
"message": "تم تطبيق Div"
|
|
591
|
+
},
|
|
592
|
+
"div_removed_d13f8e17": {
|
|
593
|
+
"message": "تمت إزالة Div"
|
|
594
|
+
},
|
|
577
595
|
"division_sign_72190870": {
|
|
578
596
|
"message": "علامة القسمة"
|
|
579
597
|
},
|
|
@@ -931,6 +949,12 @@ const locale = {
|
|
|
931
949
|
"heading_4_b2e74be7": {
|
|
932
950
|
"message": "العنوان 4"
|
|
933
951
|
},
|
|
952
|
+
"heading_h_applied_eccd294d": {
|
|
953
|
+
"message": "تم تطبيق العنوان { h }"
|
|
954
|
+
},
|
|
955
|
+
"heading_h_removed_745f24a6": {
|
|
956
|
+
"message": "تمت إزالة العنوان { h }"
|
|
957
|
+
},
|
|
934
958
|
"heading_levels_should_not_be_skipped_3947c0e0": {
|
|
935
959
|
"message": "يجب ألا يتم تجاوز مستويات العنوان."
|
|
936
960
|
},
|
|
@@ -1117,6 +1141,12 @@ const locale = {
|
|
|
1117
1141
|
"issue_num_total_f94536cf": {
|
|
1118
1142
|
"message": "مشكلة { num }/{ total }"
|
|
1119
1143
|
},
|
|
1144
|
+
"italic_applied_8f59a684": {
|
|
1145
|
+
"message": "تم تطبيق الخط المائل"
|
|
1146
|
+
},
|
|
1147
|
+
"italic_removed_17cbab6f": {
|
|
1148
|
+
"message": "تمت إزالة الخط المائل"
|
|
1149
|
+
},
|
|
1120
1150
|
"kappa_2f14c816": {
|
|
1121
1151
|
"message": "Kappa"
|
|
1122
1152
|
},
|
|
@@ -1576,6 +1606,12 @@ const locale = {
|
|
|
1576
1606
|
"paragraph_5e5ad8eb": {
|
|
1577
1607
|
"message": "الفقرة"
|
|
1578
1608
|
},
|
|
1609
|
+
"paragraph_applied_959b3fe5": {
|
|
1610
|
+
"message": "تم تطبيق الفقرة"
|
|
1611
|
+
},
|
|
1612
|
+
"paragraph_removed_d09320e": {
|
|
1613
|
+
"message": "تمت إزالة الفقرة"
|
|
1614
|
+
},
|
|
1579
1615
|
"paragraph_starting_with_start_a59923f8": {
|
|
1580
1616
|
"message": "فقرة تبدأ بـ { start }"
|
|
1581
1617
|
},
|
|
@@ -2308,6 +2344,12 @@ const locale = {
|
|
|
2308
2344
|
"unable_to_determine_resource_selection_url_7867e060": {
|
|
2309
2345
|
"message": "يتعذر تحديد عنوان URL تحديد الموارد"
|
|
2310
2346
|
},
|
|
2347
|
+
"underline_applied_ae01ec97": {
|
|
2348
|
+
"message": "تم تطبيق التسطير"
|
|
2349
|
+
},
|
|
2350
|
+
"underline_removed_3693e17c": {
|
|
2351
|
+
"message": "تمت إزالة التسطير"
|
|
2352
|
+
},
|
|
2311
2353
|
"union_e6b57a53": {
|
|
2312
2354
|
"message": "Union"
|
|
2313
2355
|
},
|
|
@@ -52,6 +52,12 @@ const locale = {
|
|
|
52
52
|
"additional_considerations_f3801683": {
|
|
53
53
|
"message": "Consideracions addicionals"
|
|
54
54
|
},
|
|
55
|
+
"address_applied_64269b0b": {
|
|
56
|
+
"message": "S’ha aplicat l’adreça"
|
|
57
|
+
},
|
|
58
|
+
"address_removed_fcb496e0": {
|
|
59
|
+
"message": "S’ha suprimit l''adreça"
|
|
60
|
+
},
|
|
55
61
|
"adjacent_links_with_the_same_url_should_be_a_singl_7a1f7f6c": {
|
|
56
62
|
"message": "Els enllaços adjacents amb el mateix URL han de constituir un sol enllaç."
|
|
57
63
|
},
|
|
@@ -208,6 +214,12 @@ const locale = {
|
|
|
208
214
|
"blue_daf8fea9": {
|
|
209
215
|
"message": "Blau"
|
|
210
216
|
},
|
|
217
|
+
"bold_applied_42d547b7": {
|
|
218
|
+
"message": "S’ha aplicat el format de negreta"
|
|
219
|
+
},
|
|
220
|
+
"bold_removed_da474a5c": {
|
|
221
|
+
"message": "S’ha suprimit el format de negreta"
|
|
222
|
+
},
|
|
211
223
|
"border_5b08b06d": {
|
|
212
224
|
"message": "Vora"
|
|
213
225
|
},
|
|
@@ -574,6 +586,12 @@ const locale = {
|
|
|
574
586
|
"display_text_link_opens_in_a_new_tab_75e9afc9": {
|
|
575
587
|
"message": "Mostra l''enllaç al text (s''obre en una pestanya nova)"
|
|
576
588
|
},
|
|
589
|
+
"div_applied_49ad83fc": {
|
|
590
|
+
"message": "S’ha aplicat la divisió"
|
|
591
|
+
},
|
|
592
|
+
"div_removed_d13f8e17": {
|
|
593
|
+
"message": "S’ha suprimit la divisió"
|
|
594
|
+
},
|
|
577
595
|
"division_sign_72190870": {
|
|
578
596
|
"message": "Signe de la divisió"
|
|
579
597
|
},
|
|
@@ -931,6 +949,12 @@ const locale = {
|
|
|
931
949
|
"heading_4_b2e74be7": {
|
|
932
950
|
"message": "Capçalera 4"
|
|
933
951
|
},
|
|
952
|
+
"heading_h_applied_eccd294d": {
|
|
953
|
+
"message": "S’ha aplicat el titular { h }"
|
|
954
|
+
},
|
|
955
|
+
"heading_h_removed_745f24a6": {
|
|
956
|
+
"message": "S’ha suprimit el titular { h }"
|
|
957
|
+
},
|
|
934
958
|
"heading_levels_should_not_be_skipped_3947c0e0": {
|
|
935
959
|
"message": "No s’han d’ometre els nivells de les capçaleres"
|
|
936
960
|
},
|
|
@@ -1117,6 +1141,12 @@ const locale = {
|
|
|
1117
1141
|
"issue_num_total_f94536cf": {
|
|
1118
1142
|
"message": "Problema { num } de { total }"
|
|
1119
1143
|
},
|
|
1144
|
+
"italic_applied_8f59a684": {
|
|
1145
|
+
"message": "S’ha aplicat el format de cursiva"
|
|
1146
|
+
},
|
|
1147
|
+
"italic_removed_17cbab6f": {
|
|
1148
|
+
"message": "S’ha suprimit el format de cursiva"
|
|
1149
|
+
},
|
|
1120
1150
|
"kappa_2f14c816": {
|
|
1121
1151
|
"message": "Kappa"
|
|
1122
1152
|
},
|
|
@@ -1576,6 +1606,12 @@ const locale = {
|
|
|
1576
1606
|
"paragraph_5e5ad8eb": {
|
|
1577
1607
|
"message": "Paràgraf"
|
|
1578
1608
|
},
|
|
1609
|
+
"paragraph_applied_959b3fe5": {
|
|
1610
|
+
"message": "S’ha aplicat el paràgraf"
|
|
1611
|
+
},
|
|
1612
|
+
"paragraph_removed_d09320e": {
|
|
1613
|
+
"message": "S’ha suprimit el paràgraf"
|
|
1614
|
+
},
|
|
1579
1615
|
"paragraph_starting_with_start_a59923f8": {
|
|
1580
1616
|
"message": "El paràgraf comença per { start }"
|
|
1581
1617
|
},
|
|
@@ -2308,6 +2344,12 @@ const locale = {
|
|
|
2308
2344
|
"unable_to_determine_resource_selection_url_7867e060": {
|
|
2309
2345
|
"message": "No s’ha pogut determinar l’URL de selecció de recursos"
|
|
2310
2346
|
},
|
|
2347
|
+
"underline_applied_ae01ec97": {
|
|
2348
|
+
"message": "S’ha aplicat el format de subratllat"
|
|
2349
|
+
},
|
|
2350
|
+
"underline_removed_3693e17c": {
|
|
2351
|
+
"message": "S’ha suprimit el format de subratllat"
|
|
2352
|
+
},
|
|
2311
2353
|
"union_e6b57a53": {
|
|
2312
2354
|
"message": "Unió"
|
|
2313
2355
|
},
|
|
@@ -52,6 +52,12 @@ const locale = {
|
|
|
52
52
|
"additional_considerations_f3801683": {
|
|
53
53
|
"message": "Ystyriaethau ychwanegol"
|
|
54
54
|
},
|
|
55
|
+
"address_applied_64269b0b": {
|
|
56
|
+
"message": "Cyfeiriad wedi’i osod"
|
|
57
|
+
},
|
|
58
|
+
"address_removed_fcb496e0": {
|
|
59
|
+
"message": "Cyfeiriad wedi’i ddileu"
|
|
60
|
+
},
|
|
55
61
|
"adjacent_links_with_the_same_url_should_be_a_singl_7a1f7f6c": {
|
|
56
62
|
"message": "Dylai dolenni cyfagos â’r un URL fod yn un ddolen."
|
|
57
63
|
},
|
|
@@ -208,6 +214,12 @@ const locale = {
|
|
|
208
214
|
"blue_daf8fea9": {
|
|
209
215
|
"message": "Glas"
|
|
210
216
|
},
|
|
217
|
+
"bold_applied_42d547b7": {
|
|
218
|
+
"message": "Fformat trwm wedi’i osod"
|
|
219
|
+
},
|
|
220
|
+
"bold_removed_da474a5c": {
|
|
221
|
+
"message": "Fformat trwm wedi’i ddileu"
|
|
222
|
+
},
|
|
211
223
|
"border_5b08b06d": {
|
|
212
224
|
"message": "Bordor"
|
|
213
225
|
},
|
|
@@ -574,6 +586,12 @@ const locale = {
|
|
|
574
586
|
"display_text_link_opens_in_a_new_tab_75e9afc9": {
|
|
575
587
|
"message": "Dangos Dolen Testun (Yn agor mewn tab newydd)"
|
|
576
588
|
},
|
|
589
|
+
"div_applied_49ad83fc": {
|
|
590
|
+
"message": "Rhaniad wedi’i osod"
|
|
591
|
+
},
|
|
592
|
+
"div_removed_d13f8e17": {
|
|
593
|
+
"message": "Rhaniad wed’i ddileu"
|
|
594
|
+
},
|
|
577
595
|
"division_sign_72190870": {
|
|
578
596
|
"message": "Arwydd Rhannu"
|
|
579
597
|
},
|
|
@@ -931,6 +949,12 @@ const locale = {
|
|
|
931
949
|
"heading_4_b2e74be7": {
|
|
932
950
|
"message": "Pennawd 4"
|
|
933
951
|
},
|
|
952
|
+
"heading_h_applied_eccd294d": {
|
|
953
|
+
"message": "Pennawd { h } wedi’i osod"
|
|
954
|
+
},
|
|
955
|
+
"heading_h_removed_745f24a6": {
|
|
956
|
+
"message": "Pennawd { h } wedi’i ddileu"
|
|
957
|
+
},
|
|
934
958
|
"heading_levels_should_not_be_skipped_3947c0e0": {
|
|
935
959
|
"message": "Ni ddylid anwybyddu lefelau penawdau."
|
|
936
960
|
},
|
|
@@ -1117,6 +1141,12 @@ const locale = {
|
|
|
1117
1141
|
"issue_num_total_f94536cf": {
|
|
1118
1142
|
"message": "Problem { num }/{ total }"
|
|
1119
1143
|
},
|
|
1144
|
+
"italic_applied_8f59a684": {
|
|
1145
|
+
"message": "Fformat italig wedi’i osod"
|
|
1146
|
+
},
|
|
1147
|
+
"italic_removed_17cbab6f": {
|
|
1148
|
+
"message": "Fformat italig wedi’i ddileu"
|
|
1149
|
+
},
|
|
1120
1150
|
"kappa_2f14c816": {
|
|
1121
1151
|
"message": "Kappa"
|
|
1122
1152
|
},
|
|
@@ -1576,6 +1606,12 @@ const locale = {
|
|
|
1576
1606
|
"paragraph_5e5ad8eb": {
|
|
1577
1607
|
"message": "Paragraff"
|
|
1578
1608
|
},
|
|
1609
|
+
"paragraph_applied_959b3fe5": {
|
|
1610
|
+
"message": "Paragraff wedi’i osod"
|
|
1611
|
+
},
|
|
1612
|
+
"paragraph_removed_d09320e": {
|
|
1613
|
+
"message": "Paragraff wedi’i ddileu"
|
|
1614
|
+
},
|
|
1579
1615
|
"paragraph_starting_with_start_a59923f8": {
|
|
1580
1616
|
"message": "Paragraff yn dechrau gyda { start }"
|
|
1581
1617
|
},
|
|
@@ -2308,6 +2344,12 @@ const locale = {
|
|
|
2308
2344
|
"unable_to_determine_resource_selection_url_7867e060": {
|
|
2309
2345
|
"message": "Does dim modd pennu url dewis adnodd"
|
|
2310
2346
|
},
|
|
2347
|
+
"underline_applied_ae01ec97": {
|
|
2348
|
+
"message": "Tanlinell wedi’i gosod"
|
|
2349
|
+
},
|
|
2350
|
+
"underline_removed_3693e17c": {
|
|
2351
|
+
"message": "Tanlinell wedi’i dileu"
|
|
2352
|
+
},
|
|
2311
2353
|
"union_e6b57a53": {
|
|
2312
2354
|
"message": "Uniad"
|
|
2313
2355
|
},
|