acquia_dam-1.0.0-rc1/js/ckeditor5_plugins/mediaRevisions/src/editing.js
js/ckeditor5_plugins/mediaRevisions/src/editing.js
import { Plugin } from 'ckeditor5/src/core'; import { Template } from 'ckeditor5/src/ui'; import UpdateMediaRevisionCommand from './command' import MediaRevisionsRepository from './repository' import { getPreviewContainer } from '../../../../../../../core/modules/ckeditor5/js/ckeditor5_plugins/drupalMedia/src/utils' export default class MediaRevisionsEditing extends Plugin { static get requires() { return ['DrupalMediaEditing', MediaRevisionsRepository]; } /** * @inheritdoc */ static get pluginName() { return 'MediaRevisionsEditing'; } init() { const {editor} = this; editor.model.schema.extend('drupalMedia', { allowAttributes: ['entityRevision', 'entityIsLatestRevision'], }); const mediaEditing = editor.plugins.get('DrupalMediaEditing'); mediaEditing.attrs['entityRevision'] = 'data-entity-revision'; const { conversion } = editor; const attributeMapping = { model: { key: 'entityRevision', name: 'drupalMedia', }, view: { name: 'drupal-media', key: 'data-entity-revision', }, }; conversion.for('dataDowncast').attributeToAttribute(attributeMapping); conversion.for('upcast') .attributeToAttribute(attributeMapping) .add((dispatcher) => { dispatcher.on( 'element:drupal-media', (event, data) => { const [modelElement] = data.modelRange.getItems(); const metadataRepository = this.editor.plugins.get( 'MediaRevisionsRepository', ); metadataRepository .getRevisionMetadata(modelElement) .then(metadata => { if (!modelElement) { return; } editor.model.enqueueChange( { isUndoable: false }, (writer) => { writer.setAttribute( 'entityIsLatestRevision', metadata.isLatest, modelElement, ); }, ); }) }, { priority: 'lowest' }, ) }) conversion.for('editingDowncast').add((dispatcher) => { // Copied from drupalmediaediting so that the preview is refreshed whenever // our embed code attribute is changed. // @todo remove after https://www.drupal.org/i/3300246. const converter = (event, data, conversionApi) => { const viewWriter = conversionApi.writer; const modelElement = data.item; const container = conversionApi.mapper.toViewElement(data.item); // Search for preview container recursively from its children because // the preview container could be wrapped with an element such as // `<a>`. let media = getPreviewContainer(container.getChildren()); // Use pre-existing media preview container if one exists. If the // preview element doesn't exist, create a new element. if (media) { // Stop processing if media preview is unavailable or a preview is // already loading. if (media.getAttribute('data-drupal-media-preview') !== 'ready') { return; } // Preview was ready meaning that a new preview can be loaded. // "Change the attribute to loading to prepare for the loading of // the updated preview. Preview is kept intact so that it remains // interactable in the UI until the new preview has been rendered. viewWriter.setAttribute( 'data-drupal-media-preview', 'loading', media, ); } else { media = viewWriter.createRawElement('div', { 'data-drupal-media-preview': 'loading', }); viewWriter.insert(viewWriter.createPositionAt(container, 0), media); } mediaEditing._fetchPreview(modelElement).then(({ label, preview }) => { if (!media) { // Nothing to do if associated preview wrapped no longer exist. return; } // CKEditor 5 doesn't support async view conversion. Therefore, once // the promise is fulfilled, the editing view needs to be modified // manually. this.editor.editing.view.change((writer) => { const mediaPreview = writer.createRawElement( 'div', { 'data-drupal-media-preview': 'ready', 'aria-label': label }, (domElement) => { domElement.innerHTML = preview; }, ); // Insert the new preview before the previous preview element to // ensure that the location remains same even if it is wrapped // with another element. writer.insert(writer.createPositionBefore(media), mediaPreview); writer.remove(media); }); }); }; dispatcher.on( 'attribute:entityRevision', (event, data) => { if (data.attributeOldValue === null || data.attributeOldValue === data.attributeNewValue) { return; } const metadataRepository = this.editor.plugins.get( 'MediaRevisionsRepository', ); metadataRepository .refreshModelMetadata(data.item) .then(metadata => { if (!data.item) { return; } editor.model.enqueueChange( { isUndoable: false }, (writer) => { writer.setAttribute( 'entityIsLatestRevision', metadata.isLatest, data.item, ); }, ); }) }, ) dispatcher.on( 'attribute:entityRevision', converter ) dispatcher.on( 'attribute:entityIsLatestRevision', (event, data, conversionApi) => { const { writer, mapper } = conversionApi; const container = mapper.toViewElement(data.item); if (data.attributeNewValue === true) { const existingError = Array.from(container.getChildren()).find( (child) => child.getCustomProperty('entityRevisionWarning'), ); if (existingError) { writer.remove(existingError) } return; } const message = Drupal.t( 'This media item has a newer version available.', ); const html = new Template({ tag: 'span', children: [ { tag: 'span', attributes: { class: 'drupal-media__mediarevision-update-icon', 'data-cke-tooltip-text': message, }, }, ], }).render(); const error = writer.createRawElement( 'div', { class: 'drupal-media__mediarevision-update', }, (domElement, domConverter) => { domConverter.setContentOf(domElement, html.outerHTML); }, ); writer.setCustomProperty('entityRevisionWarning', true, error); writer.insert(writer.createPositionAt(container, 0), error); }, { priority: 'low' }, ); }); editor.commands.add( 'updateMediaRevision', new UpdateMediaRevisionCommand(editor), ); } }