@instructure/canvas-rce 5.15.7 → 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 +4 -3
- package/coverage/canvas-rce-jest.xml +0 -7028
- package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ 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
|
+
## 6.0.0 - 2025-03-20
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Upgraded to Node 20 LTS
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Redirect focus on invalid save in Image Options tray
|
|
17
|
+
- Whitelist the aria-description attribute
|
|
18
|
+
- Flag external links when they have multi-part TLDs
|
|
19
|
+
- Save changes to alt text when it is the only thing that changes
|
|
20
|
+
- Screenreader reads out content inside raw HTML editor
|
|
21
|
+
- Validations in Upload Media modal
|
|
22
|
+
- Increased Link header size
|
|
23
|
+
|
|
24
|
+
## 5.15.8 - 2025-02-20
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Fixed invalid `querySelectorAll` selector (`:not(.not_external, .external)`)
|
|
29
|
+
that caused errors in older Chrome versions (87 and below). Updated to
|
|
30
|
+
`:not(.not_external):not(.external)` for improved browser compatibility
|
|
31
|
+
- Improved external link handling logic in canvas-rce
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Jest test to ensure the fix does not introduce regressions
|
|
36
|
+
|
|
8
37
|
## 5.15.0 - 2025-02-12
|
|
9
38
|
|
|
10
39
|
- Lazy load iframe and images by default
|
package/Dockerfile
CHANGED
package/es/bridge/Bridge.d.ts
CHANGED
|
@@ -31,6 +31,9 @@ export default class Bridge {
|
|
|
31
31
|
ADDED_CAPTION: string;
|
|
32
32
|
DELETED_CAPTION: string;
|
|
33
33
|
PROGRESS_LABEL: string;
|
|
34
|
+
SELECT_SUPPORTED_FILE_TYPE: string;
|
|
35
|
+
CHOOSE_FILE_TO_UPLOAD: string;
|
|
36
|
+
ENTER_FILE_NAME: string;
|
|
34
37
|
};
|
|
35
38
|
SelectStrings: {
|
|
36
39
|
USE_ARROWS: string;
|
|
@@ -113,6 +116,9 @@ export default class Bridge {
|
|
|
113
116
|
ADDED_CAPTION: string;
|
|
114
117
|
DELETED_CAPTION: string;
|
|
115
118
|
PROGRESS_LABEL: string;
|
|
119
|
+
SELECT_SUPPORTED_FILE_TYPE: string;
|
|
120
|
+
CHOOSE_FILE_TO_UPLOAD: string;
|
|
121
|
+
ENTER_FILE_NAME: string;
|
|
116
122
|
};
|
|
117
123
|
SelectStrings: {
|
|
118
124
|
USE_ARROWS: string;
|
|
@@ -16,6 +16,23 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
function elemsToTinyStringConfig(list) {
|
|
20
|
+
return Object.entries(list).map(pair => {
|
|
21
|
+
const [tag, attrs] = pair;
|
|
22
|
+
if (attrs.length === 0) {
|
|
23
|
+
return tag;
|
|
24
|
+
} else {
|
|
25
|
+
const attrStr = attrs.map(attr => {
|
|
26
|
+
if (typeof attr === 'string') {
|
|
27
|
+
return attr;
|
|
28
|
+
} else if (typeof attr === 'object') {
|
|
29
|
+
return Object.entries(attr).map(([key, value]) => `${key}=${value}`);
|
|
30
|
+
}
|
|
31
|
+
}).join('|');
|
|
32
|
+
return `${tag}[${attrStr}]`;
|
|
33
|
+
}
|
|
34
|
+
}).join();
|
|
35
|
+
}
|
|
19
36
|
const defaultTinymceConfig = {
|
|
20
37
|
// ============================================================================
|
|
21
38
|
// Values in this section have acceptable defaults which you may want
|
|
@@ -73,10 +90,154 @@ const defaultTinymceConfig = {
|
|
|
73
90
|
// then edited.
|
|
74
91
|
//
|
|
75
92
|
// this list needs to be kept in sync with the list in gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb
|
|
76
|
-
valid_elements:
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
valid_elements: elemsToTinyStringConfig({
|
|
94
|
+
'@': ['id', 'class', 'style', 'title', 'dir<ltr?rtl', 'lang', 'xml::lang', 'role'],
|
|
95
|
+
'a': ['rel', 'rev', 'charset', 'hreflang', 'tabindex', 'accesskey', 'type', 'name', 'href', 'target', 'title', 'class', 'data-old-link'],
|
|
96
|
+
'strong/b': [],
|
|
97
|
+
'em/i': [],
|
|
98
|
+
'strike/s': [],
|
|
99
|
+
'u': [],
|
|
100
|
+
'#p': [],
|
|
101
|
+
'-ol': ['type', 'compact'],
|
|
102
|
+
'-ul': ['type', 'compact'],
|
|
103
|
+
'-li': [],
|
|
104
|
+
'br': [],
|
|
105
|
+
'img': ['longdesc', 'usemap', 'src', 'border', 'alt', 'title', 'hspace', 'vspace', 'width', 'height', 'align', 'role', 'data-old-link'],
|
|
106
|
+
'-sub': [],
|
|
107
|
+
'-sup': [],
|
|
108
|
+
'-blockquote': ['cite'],
|
|
109
|
+
'-table': [{
|
|
110
|
+
'border': '0'
|
|
111
|
+
}, 'cellspacing', 'cellpadding', 'width', 'frame', 'rules', 'height', 'align', 'summary', 'bgcolor', 'background', 'bordercolor'],
|
|
112
|
+
'-tr': ['rowspan', 'width', 'height', 'align', 'valign', 'bgcolor', 'background', 'bordercolor'],
|
|
113
|
+
'tbody': [],
|
|
114
|
+
'thead': [],
|
|
115
|
+
'tfoot': [],
|
|
116
|
+
'#td': ['colspan', 'rowspan', 'width', 'height', 'align', 'valign', 'bgcolor', 'background', 'bordercolor', 'scope'],
|
|
117
|
+
'#th': ['colspan', 'rowspan', 'width', 'height', 'align', 'valign', 'scope'],
|
|
118
|
+
'caption': [],
|
|
119
|
+
'-div': [],
|
|
120
|
+
'-span': [],
|
|
121
|
+
'-code': [],
|
|
122
|
+
'-pre': [],
|
|
123
|
+
'address': [],
|
|
124
|
+
'-h1': [],
|
|
125
|
+
'-h2': [],
|
|
126
|
+
'-h3': [],
|
|
127
|
+
'-h4': [],
|
|
128
|
+
'-h5': [],
|
|
129
|
+
'-h6': [],
|
|
130
|
+
'hr': ['size', 'noshade'],
|
|
131
|
+
'-font': ['face', 'size', 'color'],
|
|
132
|
+
'dd': [],
|
|
133
|
+
'dl': [],
|
|
134
|
+
'dt': [],
|
|
135
|
+
'cite': [],
|
|
136
|
+
'abbr': [],
|
|
137
|
+
'acronym': [],
|
|
138
|
+
'del': ['datetime', 'cite'],
|
|
139
|
+
'ins': ['datetime', 'cite'],
|
|
140
|
+
'object': ['classid', 'width', 'height', 'codebase', '*'],
|
|
141
|
+
'param': ['name', 'value', '_value'],
|
|
142
|
+
'embed': ['type', 'width', 'height', 'src', '*'],
|
|
143
|
+
'map': ['name'],
|
|
144
|
+
'area': ['shape', 'coords', 'href', 'alt', 'target'],
|
|
145
|
+
'bdo': [],
|
|
146
|
+
'col': ['align', 'char', 'charoff', 'span', 'valign', 'width'],
|
|
147
|
+
'colgroup': ['align', 'char', 'charoff', 'span', 'valign', 'width'],
|
|
148
|
+
'dfn': [],
|
|
149
|
+
'kbd': [],
|
|
150
|
+
'q': ['cite'],
|
|
151
|
+
'samp': [],
|
|
152
|
+
'small': [],
|
|
153
|
+
'tt': [],
|
|
154
|
+
'var': [],
|
|
155
|
+
'big': [],
|
|
156
|
+
'figure': [],
|
|
157
|
+
'figcaption': [],
|
|
158
|
+
'source': ['media', 'width', 'height', 'sizes', 'src', 'srcset', 'type', 'data-old-link'],
|
|
159
|
+
'track': [],
|
|
160
|
+
'mark': [],
|
|
161
|
+
'article': [],
|
|
162
|
+
'aside': [],
|
|
163
|
+
'details': [],
|
|
164
|
+
'footer': [],
|
|
165
|
+
'header': [],
|
|
166
|
+
'nav': [],
|
|
167
|
+
'section': [],
|
|
168
|
+
'summary': [],
|
|
169
|
+
'time': []
|
|
170
|
+
}),
|
|
171
|
+
extended_valid_elements: elemsToTinyStringConfig({
|
|
172
|
+
'@': ['id', 'accesskey', 'class', 'dir', 'lang', 'style', 'tabindex', 'title', 'contenteditable', 'contextmenu', 'draggable', 'dropzone', 'hidden', 'longdesc', 'spellcheck', 'translate', 'align', 'role', 'aria-labelledby', 'aria-atomic', 'aria-busy', 'aria-controls', 'aria-describedby', 'aria-description', 'aria-disabled', 'aria-dropeffect', 'aria-flowto', 'aria-grabbed', 'aria-haspopup', 'aria-hidden', 'aria-invalid', 'aria-label', 'aria-labelledby', 'aria-live', 'aria-owns', 'aria-relevant', 'aria-autocomplete', 'aria-checked', 'aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-hidden', 'aria-invalid', 'aria-label', 'aria-level', 'aria-multiline', 'aria-multiselectable', 'aria-orientation', 'aria-pressed', 'aria-readonly', 'aria-required', 'aria-selected', 'aria-sort', 'aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
|
|
173
|
+
'iframe': ['id', 'data-media-type', 'title', 'src', 'width', 'height', 'name', 'align', 'style', 'class', 'sandbox', 'loading', 'allowfullscreen', 'webkitallowfullscreen', 'mozallowfullscreen', 'allow', 'data-old-link'],
|
|
174
|
+
'i': ['iclass'],
|
|
175
|
+
'a': ['hidden', 'href', 'target', 'rel', 'media', 'hreflang', 'type', 'charset', 'name', 'rev', 'shape', 'coords', 'download', 'alt'],
|
|
176
|
+
'#p': [],
|
|
177
|
+
'li': ['value'],
|
|
178
|
+
'-ol': ['reversed', 'start', 'type', 'compact'],
|
|
179
|
+
'pre': ['width'],
|
|
180
|
+
'table': ['border', 'summary', 'width', 'frame', 'rules', 'cellspacing', 'cellpadding', 'bgcolor'],
|
|
181
|
+
'tbody': ['char', 'charoff', 'valign'],
|
|
182
|
+
'td': ['colspan', 'rowspan', 'headers', 'abbr', 'axis', 'scope', 'align', 'char', 'charoff', 'valign', 'nowrap', 'bgcolor', 'width', 'height'],
|
|
183
|
+
'tfoot': ['char', 'charoff', 'valign'],
|
|
184
|
+
'th': ['colspan', 'rowspan', 'headers', 'scope', 'abbr', 'axis', 'align', 'char', 'charoff', 'valign', 'nowrap', 'bgcolor', 'width', 'height'],
|
|
185
|
+
'thead': ['char', 'charoff', 'valign'],
|
|
186
|
+
'tr': ['char', 'charoff', 'valign', 'bgcolor'],
|
|
187
|
+
'-ul': ['compact'],
|
|
188
|
+
'video': ['name', 'src', 'allowfullscreen', 'muted', 'poster', 'width', 'height', 'controls', 'playsinline'],
|
|
189
|
+
'audio': ['name', 'src', 'muted', 'controls'],
|
|
190
|
+
'annotation': ['href', 'xref', 'definitionURL', 'encoding', 'cd', 'name', 'src'],
|
|
191
|
+
'annotation-xml': ['href', 'xref', 'definitionURL', 'encoding', 'cd', 'name', 'src'],
|
|
192
|
+
'maction': ['href', 'xref', 'mathcolor', 'mathbackground', 'actiontype', 'selection'],
|
|
193
|
+
'maligngroup': ['href', 'xref', 'mathcolor', 'mathbackground', 'groupalign'],
|
|
194
|
+
'malignmark': ['href', 'xref', 'mathcolor', 'mathbackground', 'edge'],
|
|
195
|
+
'math': ['xmlns', 'href', 'xref', 'display', 'maxwidth', 'overflow', 'altimg', 'altimg-width', 'altimg-height', 'altimg-valign', 'alttext', 'cdgroup', 'mathcolor', 'mathbackground', 'scriptlevel', 'displaystyle', 'scriptsizemultiplier', 'scriptminsize', 'infixlinebreakstyle', 'decimalpoint', 'mathvariant', 'mathsize', 'width', 'height', 'valign', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast', 'depth', 'lquote', 'rquote', 'linethickness', 'munalign', 'denomalign', 'bevelled', 'voffset', 'open', 'close', 'separators', 'notation', 'subscriptshift', 'superscriptshift', 'accentunder', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'side', 'minlabelspacing', 'rowspan', 'columnspan', 'edge', 'stackalign', 'charalign', 'charspacing', 'longdivstyle', 'position', 'shift', 'location', 'crossout', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness', 'selection'],
|
|
196
|
+
'menclose': ['href', 'xref', 'mathcolor', 'mathbackground', 'notation'],
|
|
197
|
+
'merror': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
198
|
+
'mfenced': ['href', 'xref', 'mathcolor', 'mathbackground', 'open', 'close', 'separators'],
|
|
199
|
+
'mfrac': ['href', 'xref', 'mathcolor', 'mathbackground', 'linethickness', 'munalign', 'denomalign', 'bevelled'],
|
|
200
|
+
'mglyph': ['href', 'xref', 'mathcolor', 'mathbackground', 'src', 'alt', 'width', 'height', 'valign'],
|
|
201
|
+
'mi': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
|
|
202
|
+
'mlabeledtr': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
203
|
+
'mlongdiv': ['href', 'xref', 'mathcolor', 'mathbackground', 'longdivstyle', 'align', 'stackalign', 'charalign', 'charspacing'],
|
|
204
|
+
'mmultiscripts': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift', 'superscriptshift'],
|
|
205
|
+
'mn': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
|
|
206
|
+
'mo': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast'],
|
|
207
|
+
'mover': ['href', 'xref', 'mathcolor', 'mathbackground', 'accent', 'align'],
|
|
208
|
+
'mpadded': ['href', 'xref', 'mathcolor', 'mathbackground', 'height', 'depth', 'width', 'lspace', 'voffset'],
|
|
209
|
+
'mphantom': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
210
|
+
'mprescripts': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
211
|
+
'mroot': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
212
|
+
'mrow': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
213
|
+
'ms': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'lquote', 'rquote'],
|
|
214
|
+
'mscarries': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'location', 'crossout', 'scriptsizemultiplier'],
|
|
215
|
+
'mscarry': ['href', 'xref', 'mathcolor', 'mathbackground', 'location', 'crossout'],
|
|
216
|
+
'msgroup': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'shift'],
|
|
217
|
+
'msline': ['href', 'xref', 'mathcolor', 'mathbackground', 'position', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness'],
|
|
218
|
+
'mspace': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize'],
|
|
219
|
+
'msqrt': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
220
|
+
'msrow': ['href', 'xref', 'mathcolor', 'mathbackground', 'position'],
|
|
221
|
+
'mstack': ['href', 'xref', 'mathcolor', 'mathbackground', 'align', 'stackalign', 'charalign', 'charspacing'],
|
|
222
|
+
'mstyle': ['href', 'xref', 'mathcolor', 'mathbackground', 'scriptlevel', 'displaystyle', 'scriptsizemultiplier', 'scriptminsize', 'infixlinebreakstyle', 'decimalpoint', 'mathvariant', 'mathsize', 'width', 'height', 'valign', 'form', 'fence', 'separator', 'lspace', 'rspace', 'stretchy', 'symmetric', 'maxsize', 'minsize', 'largeop', 'movablelimits', 'accent', 'linebreak', 'lineleading', 'linebreakstyle', 'linebreakmultchar', 'indentalign', 'indentshift', 'indenttarget', 'indentalignfirst', 'indentshiftfirst', 'indentalignlast', 'indentshiftlast', 'depth', 'lquote', 'rquote', 'linethickness', 'munalign', 'denomalign', 'bevelled', 'voffset', 'open', 'close', 'separators', 'notation', 'subscriptshift', 'superscriptshift', 'accentunder', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'side', 'minlabelspacing', 'rowspan', 'columnspan', 'edge', 'stackalign', 'charalign', 'charspacing', 'longdivstyle', 'position', 'shift', 'location', 'crossout', 'length', 'leftoverhang', 'rightoverhang', 'mslinethickness', 'selection'],
|
|
223
|
+
'msub': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift'],
|
|
224
|
+
'msubsup': ['href', 'xref', 'mathcolor', 'mathbackground', 'subscriptshift', 'superscriptshift'],
|
|
225
|
+
'msup': ['href', 'xref', 'mathcolor', 'mathbackground', 'superscriptshift'],
|
|
226
|
+
'mtable': ['href', 'xref', 'mathcolor', 'mathbackground', 'align', 'rowalign', 'columnalign', 'groupalign', 'alignmentscope', 'columnwidth', 'width', 'rowspacing', 'columnspacing', 'rowlines', 'columnlines', 'frame', 'framespacing', 'equalrows', 'equalcolumns', 'displaystyle', 'side', 'minlabelspacing'],
|
|
227
|
+
'mtd': ['href', 'xref', 'mathcolor', 'mathbackground', 'rowspan', 'columnspan', 'rowalign', 'columnalign', 'groupalign'],
|
|
228
|
+
'mtext': ['href', 'xref', 'mathcolor', 'mathbackground', 'mathvariant', 'mathsize', 'width', 'height', 'depth', 'linebreak'],
|
|
229
|
+
'mtr': ['href', 'xref', 'mathcolor', 'mathbackground', 'rowalign', 'columnalign', 'groupalign'],
|
|
230
|
+
'munder': ['href', 'xref', 'mathcolor', 'mathbackground', 'accentunder', 'align'],
|
|
231
|
+
'munderover': ['href', 'xref', 'mathcolor', 'mathbackground', 'accent', 'accentunder', 'align'],
|
|
232
|
+
'none': ['href', 'xref', 'mathcolor', 'mathbackground'],
|
|
233
|
+
'semantics': ['href', 'xref', 'definitionURL', 'encoding'],
|
|
234
|
+
'picture': [],
|
|
235
|
+
'ruby': [],
|
|
236
|
+
'rp': [],
|
|
237
|
+
'rt': [],
|
|
238
|
+
'g': ['*'],
|
|
239
|
+
'circle': ['*']
|
|
240
|
+
}),
|
|
80
241
|
non_empty_elements: 'td th iframe video audio object script a i area base basefont br col frame hr img input isindex link meta param embed source wbr track ruby',
|
|
81
242
|
// tiny's external link create/edit dialog config
|
|
82
243
|
target_list: false,
|
|
@@ -242,7 +242,7 @@ export function enhanceUserContent(container = document, opts = {}) {
|
|
|
242
242
|
iframeElem.setAttribute('src', src.replace('display=in_rce', 'display=borderless'));
|
|
243
243
|
}
|
|
244
244
|
});
|
|
245
|
-
unenhanced_elem.querySelectorAll('a:not(.not_external
|
|
245
|
+
unenhanced_elem.querySelectorAll('a:not(.not_external):not(.external)').forEach(childLink => {
|
|
246
246
|
if (!isExternalLink(childLink, canvasOrigin)) return;
|
|
247
247
|
if (childLink.tagName === 'IMG' || childLink.querySelectorAll('img').length > 0) return;
|
|
248
248
|
childLink.classList.add('external');
|
|
@@ -21,6 +21,7 @@ import { showFlashAlert } from '../common/FlashAlert';
|
|
|
21
21
|
import { isPreviewable, loadDocPreview, removeLoadingImage, showLoadingImage } from './doc_previews';
|
|
22
22
|
import { show } from './jqueryish_funcs';
|
|
23
23
|
import { parseUrlOrNull } from '../util/url-util';
|
|
24
|
+
import psl from 'psl';
|
|
24
25
|
const youTubeRegEx = /^https?:\/\/(www\.youtube\.com\/watch.*v(=|\/)|youtu\.be\/)([^&#]*)/;
|
|
25
26
|
export function youTubeID(path) {
|
|
26
27
|
const match = path.match(youTubeRegEx);
|
|
@@ -31,9 +32,12 @@ export function youTubeID(path) {
|
|
|
31
32
|
}
|
|
32
33
|
export function getTld(hostname) {
|
|
33
34
|
hostname = (hostname || '').split(':')[0];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (hostname.includes('inseng.test')) {
|
|
36
|
+
const parts = hostname.split('.');
|
|
37
|
+
return parts.slice(-3).join('.');
|
|
38
|
+
}
|
|
39
|
+
const parsed = psl.parse(hostname);
|
|
40
|
+
return parsed.domain || hostname;
|
|
37
41
|
}
|
|
38
42
|
export function isExternalLink(element, canvasOrigin = window.location.origin) {
|
|
39
43
|
let canvasHost;
|
package/es/rce/RCEWrapper.js
CHANGED
|
@@ -63,6 +63,7 @@ import buildStyle from './style';
|
|
|
63
63
|
import { getMenubarForVariant, getMenuForVariant, getToolbarForVariant, getStatusBarFeaturesForVariant } from './RCEVariants';
|
|
64
64
|
import { focusFirstMenuButton, focusToolbar, isElementWithinTable, mergeMenu, mergeMenuItems, mergePlugins, mergeToolbar, parsePluginsToExclude, patchAutosavedContent } from './RCEWrapper.utils';
|
|
65
65
|
import { externalToolsForToolbar } from './plugins/instructure_rce_external_tools/util/externalToolsForToolbar';
|
|
66
|
+
import { initScreenreaderOnFormat } from './screenreaderOnFormat';
|
|
66
67
|
const RestoreAutoSaveModal = /*#__PURE__*/React.lazy(() => import('./RestoreAutoSaveModal'));
|
|
67
68
|
const RceHtmlEditor = /*#__PURE__*/React.lazy(() => import('./RceHtmlEditor'));
|
|
68
69
|
const ASYNC_FOCUS_TIMEOUT = 250;
|
|
@@ -311,6 +312,7 @@ class RCEWrapper extends React.Component {
|
|
|
311
312
|
// @ts-expect-error
|
|
312
313
|
textarea.value = this.getCode();
|
|
313
314
|
textarea.style.height = this.state.height;
|
|
315
|
+
textarea.removeAttribute('aria-hidden');
|
|
314
316
|
if (document.body.classList.contains('Underline-All-Links__enabled')) {
|
|
315
317
|
if (this.iframe?.contentDocument) {
|
|
316
318
|
this.iframe.contentDocument.body.classList.add('Underline-All-Links__enabled');
|
|
@@ -350,6 +352,7 @@ class RCEWrapper extends React.Component {
|
|
|
350
352
|
// focus-trapping components, so they properly ignore trapping focus on click.
|
|
351
353
|
editor.on('click', () => window.document.body.click(), true);
|
|
352
354
|
editor.on('Cut Change input Undo Redo', debounce(this.handleInputChange, 1000));
|
|
355
|
+
initScreenreaderOnFormat(editor);
|
|
353
356
|
this.announceContextToolbars(editor);
|
|
354
357
|
if (this.isAutoSaving) {
|
|
355
358
|
this.initAutoSave(editor);
|
|
@@ -20,7 +20,7 @@ import React from 'react';
|
|
|
20
20
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
21
21
|
import { cleanUrl } from './contentInsertionUtils';
|
|
22
22
|
import formatMessage from '../format-message';
|
|
23
|
-
import {
|
|
23
|
+
import { videoDefaultSize, AUDIO_PLAYER_SIZE } from './plugins/instructure_record/VideoOptionsTray/TrayController';
|
|
24
24
|
import { mediaPlayerURLFromFile } from './plugins/shared/fileTypeUtils';
|
|
25
25
|
import { prepEmbedSrc, prepLinkedSrc, absoluteToRelativeUrl } from '../common/fileUrl';
|
|
26
26
|
export function renderLink(data, contents, canvasOrigin) {
|
|
@@ -97,6 +97,7 @@ export function renderImage(image, canvasOrigin, opts) {
|
|
|
97
97
|
}
|
|
98
98
|
export function renderVideo(video, canvasOrigin) {
|
|
99
99
|
const src = mediaPlayerURLFromFile(video, canvasOrigin);
|
|
100
|
+
const videoSize = videoDefaultSize();
|
|
100
101
|
return `
|
|
101
102
|
<iframe
|
|
102
103
|
allow="fullscreen"
|
|
@@ -105,7 +106,7 @@ export function renderVideo(video, canvasOrigin) {
|
|
|
105
106
|
data-media-type="video"
|
|
106
107
|
loading="lazy"
|
|
107
108
|
src="${src}"
|
|
108
|
-
style="width:${
|
|
109
|
+
style="width:${videoSize.width};height:${videoSize.height};display:inline-block;"
|
|
109
110
|
title="${formatMessage('Video player for {title}', {
|
|
110
111
|
title: video.title || video.name || video.text
|
|
111
112
|
})}"></iframe>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (C)
|
|
2
|
+
* Copyright (C) 2025 - present Instructure, Inc.
|
|
3
3
|
*
|
|
4
4
|
* This file is part of Canvas.
|
|
5
5
|
*
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import React, { useState, useEffect } from 'react';
|
|
19
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
20
20
|
import { bool, func, number, shape, string } from 'prop-types';
|
|
21
21
|
import { Button, CloseButton } from '@instructure/ui-buttons';
|
|
22
22
|
import { Heading } from '@instructure/ui-heading';
|
|
@@ -60,6 +60,17 @@ export default function ImageOptionsTray(props) {
|
|
|
60
60
|
minWidth: MIN_WIDTH,
|
|
61
61
|
minPercentage: MIN_PERCENTAGE
|
|
62
62
|
});
|
|
63
|
+
const [altHasError, setAltHasError] = useState(false);
|
|
64
|
+
const [urlHasError, setUrlHasError] = useState(false);
|
|
65
|
+
const [dimensionsHasError, setDimensionsHasError] = useState(false);
|
|
66
|
+
const urlRef = useRef(null);
|
|
67
|
+
const altRef = useRef(null);
|
|
68
|
+
const dimensionsRef = useRef(null);
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
setUrlHasError(showUrlField && url === '');
|
|
71
|
+
setAltHasError(displayAs === 'embed' && !isDecorativeImage && altText === '');
|
|
72
|
+
setDimensionsHasError(imageSize === CUSTOM && !dimensionsState?.isValid);
|
|
73
|
+
}, [isDecorativeImage, altText, url, displayAs, showUrlField, imageSize, dimensionsState?.isValid]);
|
|
63
74
|
function handleUrlChange(newUrl) {
|
|
64
75
|
setUrl(newUrl);
|
|
65
76
|
}
|
|
@@ -72,7 +83,7 @@ export default function ImageOptionsTray(props) {
|
|
|
72
83
|
function handleDisplayAsChange(event) {
|
|
73
84
|
setDisplayAs(event.target.value);
|
|
74
85
|
}
|
|
75
|
-
function handleImageSizeChange(
|
|
86
|
+
function handleImageSizeChange(_e, selectedOption) {
|
|
76
87
|
setImageSize(selectedOption.value);
|
|
77
88
|
if (selectedOption.value === CUSTOM) {
|
|
78
89
|
setImageHeight(currentHeight);
|
|
@@ -88,6 +99,18 @@ export default function ImageOptionsTray(props) {
|
|
|
88
99
|
}
|
|
89
100
|
function handleSave(event) {
|
|
90
101
|
event.preventDefault();
|
|
102
|
+
if (urlHasError || altHasError || dimensionsHasError) {
|
|
103
|
+
if (altHasError) {
|
|
104
|
+
altRef?.current?.focus();
|
|
105
|
+
}
|
|
106
|
+
if (urlHasError) {
|
|
107
|
+
urlRef?.current?.focus();
|
|
108
|
+
}
|
|
109
|
+
if (dimensionsHasError) {
|
|
110
|
+
dimensionsRef?.current?.focus();
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
91
114
|
const savedAltText = isDecorativeImage ? '' : altText;
|
|
92
115
|
let appliedHeight = imageHeight;
|
|
93
116
|
let appliedWidth = imageWidth;
|
|
@@ -134,9 +157,6 @@ export default function ImageOptionsTray(props) {
|
|
|
134
157
|
type: 'hint'
|
|
135
158
|
});
|
|
136
159
|
}
|
|
137
|
-
const disableForIcons = isIconMaker && !isDecorativeImage && altText === '';
|
|
138
|
-
const disableForImages = url === '' || displayAs === 'embed' && (!isDecorativeImage && altText === '' || imageSize === CUSTOM && !dimensionsState?.isValid);
|
|
139
|
-
const saveDisabled = isIconMaker ? disableForIcons : disableForImages;
|
|
140
160
|
const trayLabel = isIconMaker ? formatMessage('Icon Options Tray') : formatMessage('Image Options Tray');
|
|
141
161
|
const trayHeading = isIconMaker ? formatMessage('Icon Options') : formatMessage('Image Options');
|
|
142
162
|
return /*#__PURE__*/React.createElement(Tray, {
|
|
@@ -183,7 +203,9 @@ export default function ImageOptionsTray(props) {
|
|
|
183
203
|
padding: "small"
|
|
184
204
|
}, /*#__PURE__*/React.createElement(UrlPanel, {
|
|
185
205
|
fileUrl: url,
|
|
186
|
-
|
|
206
|
+
urlRef: urlRef,
|
|
207
|
+
setFileUrl: handleUrlChange,
|
|
208
|
+
urlHasError: urlHasError
|
|
187
209
|
})), /*#__PURE__*/React.createElement(ImageOptionsForm, {
|
|
188
210
|
id: "image-options-form",
|
|
189
211
|
imageSize: imageSize,
|
|
@@ -197,14 +219,16 @@ export default function ImageOptionsTray(props) {
|
|
|
197
219
|
handleDisplayAsChange: handleDisplayAsChange,
|
|
198
220
|
handleImageSizeChange: handleImageSizeChange,
|
|
199
221
|
messagesForSize: messagesForSize,
|
|
200
|
-
isIconMaker: isIconMaker
|
|
222
|
+
isIconMaker: isIconMaker,
|
|
223
|
+
altHasError: altHasError,
|
|
224
|
+
altRef: altRef,
|
|
225
|
+
dimensionsRef: dimensionsRef
|
|
201
226
|
})), /*#__PURE__*/React.createElement(Flex.Item, {
|
|
202
227
|
background: "secondary",
|
|
203
228
|
borderWidth: "small none none none",
|
|
204
229
|
padding: "small medium",
|
|
205
230
|
textAlign: "end"
|
|
206
231
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
207
|
-
disabled: saveDisabled,
|
|
208
232
|
onClick: handleSave,
|
|
209
233
|
color: "primary"
|
|
210
234
|
}, formatMessage('Done')))))));
|
|
@@ -33,7 +33,7 @@ export declare class RceToolWrapper {
|
|
|
33
33
|
width: import("prop-types").Requireable<number>;
|
|
34
34
|
use_tray: import("prop-types").Requireable<boolean>;
|
|
35
35
|
canvas_icon_class: import("prop-types").Requireable<any>;
|
|
36
|
-
}, "
|
|
36
|
+
}, "id" | "name" | "width" | "height" | "description" | "favorite" | "on_by_default" | "icon_url" | "use_tray" | "canvas_icon_class">>>)[], mruIds?: string[]): RceToolWrapper[];
|
|
37
37
|
readonly iconId: string | null | undefined;
|
|
38
38
|
isMruTool: boolean;
|
|
39
39
|
get editor(): import("../../types").ExternalToolsEditor | null;
|
|
@@ -3,12 +3,22 @@ export namespace VIDEO_SIZE_DEFAULT {
|
|
|
3
3
|
let height: string;
|
|
4
4
|
let width: string;
|
|
5
5
|
}
|
|
6
|
-
export namespace
|
|
7
|
-
let width_1: string;
|
|
8
|
-
export { width_1 as width };
|
|
6
|
+
export namespace STUDIO_PLAYER_VIDEO_SIZE_DEFAULT {
|
|
9
7
|
let height_1: string;
|
|
10
8
|
export { height_1 as height };
|
|
9
|
+
let width_1: string;
|
|
10
|
+
export { width_1 as width };
|
|
11
|
+
}
|
|
12
|
+
export namespace AUDIO_PLAYER_SIZE {
|
|
13
|
+
let width_2: string;
|
|
14
|
+
export { width_2 as width };
|
|
15
|
+
let height_2: string;
|
|
16
|
+
export { height_2 as height };
|
|
11
17
|
}
|
|
18
|
+
export function videoDefaultSize(): {
|
|
19
|
+
height: string;
|
|
20
|
+
width: string;
|
|
21
|
+
};
|
|
12
22
|
export default class TrayController {
|
|
13
23
|
_editor: any;
|
|
14
24
|
_isOpen: boolean;
|
|
@@ -28,10 +28,20 @@ export const VIDEO_SIZE_DEFAULT = {
|
|
|
28
28
|
height: '225px',
|
|
29
29
|
width: '400px'
|
|
30
30
|
}; // AKA "LARGE"
|
|
31
|
+
export const STUDIO_PLAYER_VIDEO_SIZE_DEFAULT = {
|
|
32
|
+
height: '300px',
|
|
33
|
+
width: '480px'
|
|
34
|
+
};
|
|
31
35
|
export const AUDIO_PLAYER_SIZE = {
|
|
32
36
|
width: '320px',
|
|
33
37
|
height: '14.25rem'
|
|
34
38
|
};
|
|
39
|
+
export const videoDefaultSize = () => {
|
|
40
|
+
if (RCEGlobals.getFeatures().consolidated_media_player) {
|
|
41
|
+
return STUDIO_PLAYER_VIDEO_SIZE_DEFAULT;
|
|
42
|
+
}
|
|
43
|
+
return VIDEO_SIZE_DEFAULT;
|
|
44
|
+
};
|
|
35
45
|
export default class TrayController {
|
|
36
46
|
constructor() {
|
|
37
47
|
this._editor = null;
|
|
@@ -25,6 +25,9 @@ declare namespace uploadMediaTranslations {
|
|
|
25
25
|
let ADDED_CAPTION: string;
|
|
26
26
|
let DELETED_CAPTION: string;
|
|
27
27
|
let PROGRESS_LABEL: string;
|
|
28
|
+
let SELECT_SUPPORTED_FILE_TYPE: string;
|
|
29
|
+
let CHOOSE_FILE_TO_UPLOAD: string;
|
|
30
|
+
let ENTER_FILE_NAME: string;
|
|
28
31
|
}
|
|
29
32
|
namespace SelectStrings {
|
|
30
33
|
let USE_ARROWS: string;
|
|
@@ -46,7 +46,10 @@ const uploadMediaTranslations = {
|
|
|
46
46
|
ADD_NEW_CAPTION_OR_SUBTITLE: formatMessage('Add another'),
|
|
47
47
|
ADDED_CAPTION: 'Captions for {lang} added',
|
|
48
48
|
DELETED_CAPTION: 'Deleted captions for {lang}',
|
|
49
|
-
PROGRESS_LABEL: formatMessage('Uploading')
|
|
49
|
+
PROGRESS_LABEL: formatMessage('Uploading'),
|
|
50
|
+
SELECT_SUPPORTED_FILE_TYPE: formatMessage('Please select a file of a supported type'),
|
|
51
|
+
CHOOSE_FILE_TO_UPLOAD: formatMessage('Please choose a file'),
|
|
52
|
+
ENTER_FILE_NAME: formatMessage('Please enter a file name')
|
|
50
53
|
},
|
|
51
54
|
SelectStrings: {
|
|
52
55
|
USE_ARROWS: 'Use arrow keys to navigate options.',
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
declare function DimensionInput(props: any): React.JSX.Element;
|
|
2
2
|
declare namespace DimensionInput {
|
|
3
3
|
namespace propTypes {
|
|
4
|
-
let dimensionState: import("prop-types").Requireable<import("prop-types").InferProps<{
|
|
4
|
+
export let dimensionState: import("prop-types").Requireable<import("prop-types").InferProps<{
|
|
5
5
|
addOffset: import("prop-types").Validator<(...args: any[]) => any>;
|
|
6
6
|
inputValue: import("prop-types").Validator<string>;
|
|
7
7
|
setInputValue: import("prop-types").Validator<(...args: any[]) => any>;
|
|
8
8
|
}>>;
|
|
9
|
-
|
|
9
|
+
export { object as dimensionsRef };
|
|
10
|
+
export let label: import("prop-types").Validator<string>;
|
|
10
11
|
}
|
|
11
12
|
}
|
|
12
13
|
export default DimensionInput;
|
|
13
14
|
import React from 'react';
|
|
15
|
+
import { object } from 'prop-types';
|
|
@@ -17,14 +17,15 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import React from 'react';
|
|
20
|
-
import { func, shape, string } from 'prop-types';
|
|
20
|
+
import { func, shape, string, object } from 'prop-types';
|
|
21
21
|
import { ScreenReaderContent } from '@instructure/ui-a11y-content';
|
|
22
22
|
import { NumberInput } from '@instructure/ui-number-input';
|
|
23
23
|
export default function DimensionInput(props) {
|
|
24
24
|
const {
|
|
25
25
|
dimensionState,
|
|
26
26
|
label,
|
|
27
|
-
messages
|
|
27
|
+
messages,
|
|
28
|
+
dimensionsRef
|
|
28
29
|
} = props;
|
|
29
30
|
const {
|
|
30
31
|
addOffset,
|
|
@@ -49,7 +50,12 @@ export default function DimensionInput(props) {
|
|
|
49
50
|
isRequired: true,
|
|
50
51
|
showArrows: false,
|
|
51
52
|
value: inputValue,
|
|
52
|
-
messages: messages
|
|
53
|
+
messages: messages,
|
|
54
|
+
inputRef: ref => {
|
|
55
|
+
if (dimensionsRef) {
|
|
56
|
+
dimensionsRef.current = ref;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
53
59
|
});
|
|
54
60
|
}
|
|
55
61
|
DimensionInput.propTypes = {
|
|
@@ -58,5 +64,6 @@ DimensionInput.propTypes = {
|
|
|
58
64
|
inputValue: string.isRequired,
|
|
59
65
|
setInputValue: func.isRequired
|
|
60
66
|
}),
|
|
67
|
+
dimensionsRef: object,
|
|
61
68
|
label: string.isRequired
|
|
62
69
|
};
|
|
@@ -25,6 +25,7 @@ declare namespace DimensionsInput {
|
|
|
25
25
|
export let minWidth: import("prop-types").Validator<number>;
|
|
26
26
|
export let minPercentage: import("prop-types").Validator<number>;
|
|
27
27
|
export { bool as hidePercentage };
|
|
28
|
+
export { object as dimensionsRef };
|
|
28
29
|
}
|
|
29
30
|
namespace defaultProps {
|
|
30
31
|
let hidePercentage: boolean;
|
|
@@ -34,3 +35,4 @@ export default DimensionsInput;
|
|
|
34
35
|
export { default as useDimensionsState } from "./useDimensionsState";
|
|
35
36
|
import React from 'react';
|
|
36
37
|
import { bool } from 'prop-types';
|
|
38
|
+
import { object } from 'prop-types';
|