2 * Handle alignment for embed (iframe/video) content.
3 * TinyMCE built-in handling doesn't work well for these when classes are used for
4 * alignment, since the editor wraps these elements in a non-editable preview span
5 * which looses tracking and setting of alignment options.
6 * Here we manually manage these properties and formatting events, by effectively
7 * syncing the alignment classes to the parent preview span.
8 * @param {Editor} editor
10 export function handleEmbedAlignmentChanges(editor) {
11 function updateClassesForPreview(previewElem) {
12 const mediaTarget = previewElem.querySelector('iframe, video');
17 const alignmentClasses = [...mediaTarget.classList.values()].filter(c => c.startsWith('align-'));
18 const previewAlignClasses = [...previewElem.classList.values()].filter(c => c.startsWith('align-'));
19 previewElem.classList.remove(...previewAlignClasses);
20 previewElem.classList.add(...alignmentClasses);
23 editor.on('SetContent', () => {
24 const previewElems = editor.dom.select('span.mce-preview-object');
25 for (const previewElem of previewElems) {
26 updateClassesForPreview(previewElem);
30 editor.on('FormatApply', event => {
31 const isAlignment = event.format.startsWith('align');
32 if (!event.node || !event.node.matches('.mce-preview-object')) {
36 const realTarget = event.node.querySelector('iframe, video');
37 if (isAlignment && realTarget) {
38 const className = (editor.formatter.get(event.format)[0]?.classes || [])[0];
39 const toAdd = !realTarget.classList.contains(className);
41 const wrapperClasses = (event.node.getAttribute('data-mce-p-class') || '').split(' ');
42 const wrapperClassesFiltered = wrapperClasses.filter(c => !c.startsWith('align-'));
44 wrapperClassesFiltered.push(className);
47 const classesToApply = wrapperClassesFiltered.join(' ');
48 event.node.setAttribute('data-mce-p-class', classesToApply);
50 realTarget.setAttribute('class', classesToApply);
51 editor.formatter.apply(event.format, {}, realTarget);
52 updateClassesForPreview(event.node);