|
@@ -9,10 +9,14 @@
|
|
|
|
|
|
|
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
|
|
|
import ModelTreeWalker from '@ckeditor/ckeditor5-engine/src/model/treewalker';
|
|
import ModelTreeWalker from '@ckeditor/ckeditor5-engine/src/model/treewalker';
|
|
|
|
|
+import { insertElement } from '@ckeditor/ckeditor5-engine/src/conversion/model-to-view-converters';
|
|
|
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
|
|
import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
|
|
|
|
|
+import ViewContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement';
|
|
|
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
|
|
import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
|
|
|
import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
|
|
import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
|
|
|
|
|
+import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
|
|
|
import ViewMatcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
|
|
import ViewMatcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
|
|
|
|
|
+import { isImage } from '../utils';
|
|
|
|
|
|
|
|
export default class ImageCaptioningEngine extends Plugin {
|
|
export default class ImageCaptioningEngine extends Plugin {
|
|
|
/**
|
|
/**
|
|
@@ -23,30 +27,42 @@ export default class ImageCaptioningEngine extends Plugin {
|
|
|
const document = editor.document;
|
|
const document = editor.document;
|
|
|
const schema = document.schema;
|
|
const schema = document.schema;
|
|
|
const data = editor.data;
|
|
const data = editor.data;
|
|
|
|
|
+ const editing = editor.editing;
|
|
|
|
|
|
|
|
// Schema configuration.
|
|
// Schema configuration.
|
|
|
schema.registerItem( 'caption' );
|
|
schema.registerItem( 'caption' );
|
|
|
schema.allow( { name: '$inline', inside: 'caption' } );
|
|
schema.allow( { name: '$inline', inside: 'caption' } );
|
|
|
schema.allow( { name: 'caption', inside: 'image' } );
|
|
schema.allow( { name: 'caption', inside: 'image' } );
|
|
|
|
|
|
|
|
- // Add caption element to each image without it.
|
|
|
|
|
- // document.on( 'change', insertCaptionElement );
|
|
|
|
|
|
|
+ // Add caption element to each image inserted without it.
|
|
|
|
|
+ document.on( 'change', insertCaptionElement );
|
|
|
|
|
|
|
|
// View to model converter for data pipeline.
|
|
// View to model converter for data pipeline.
|
|
|
- // const matcher = new ViewMatcher( ( element ) => {
|
|
|
|
|
- // const parent = element.parent;
|
|
|
|
|
- //
|
|
|
|
|
- // if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'image' ) ) {
|
|
|
|
|
- // return { name: true };
|
|
|
|
|
- // }
|
|
|
|
|
- //
|
|
|
|
|
- // return null;
|
|
|
|
|
- // } );
|
|
|
|
|
-
|
|
|
|
|
- // buildViewConverter()
|
|
|
|
|
- // .for( data.viewToModel )
|
|
|
|
|
- // .fromElement( 'figcaption' )
|
|
|
|
|
- // .toElement( 'caption' );
|
|
|
|
|
|
|
+ const matcher = new ViewMatcher( ( element ) => {
|
|
|
|
|
+ const parent = element.parent;
|
|
|
|
|
+
|
|
|
|
|
+ // Convert only captions for images.
|
|
|
|
|
+ if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'image' ) ) {
|
|
|
|
|
+ return { name: true };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null;
|
|
|
|
|
+ } );
|
|
|
|
|
+
|
|
|
|
|
+ buildViewConverter()
|
|
|
|
|
+ .for( data.viewToModel )
|
|
|
|
|
+ .from( matcher )
|
|
|
|
|
+ .toElement( 'caption' );
|
|
|
|
|
+
|
|
|
|
|
+ // Model to view converter for editing pipeline.
|
|
|
|
|
+ const insertConverter = insertElement( new ViewContainerElement( 'figcaption', { contenteditable: true } ) );
|
|
|
|
|
+ editing.modelToView.on( 'insert:caption', ( evt, data, consumable, conversionApi ) => {
|
|
|
|
|
+ const captionElement = data.item;
|
|
|
|
|
+
|
|
|
|
|
+ if ( isImage( captionElement.parent ) ) {
|
|
|
|
|
+ insertConverter( evt, data, consumable, conversionApi );
|
|
|
|
|
+ }
|
|
|
|
|
+ } );
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -65,8 +81,9 @@ function insertCaptionElement( evt, changeType, data, batch ) {
|
|
|
|
|
|
|
|
if ( value.type == 'elementStart' && item.name == 'image' && !hasCaption( item ) ) {
|
|
if ( value.type == 'elementStart' && item.name == 'image' && !hasCaption( item ) ) {
|
|
|
// Using batch of insertion.
|
|
// Using batch of insertion.
|
|
|
- // TODO: Should I use enqChanges?
|
|
|
|
|
- batch.insert( ModelPosition.createAt( item, 'end' ), new ModelElement( 'caption' ) );
|
|
|
|
|
|
|
+ batch.document.enqueueChanges( () => {
|
|
|
|
|
+ batch.insert( ModelPosition.createAt( item, 'end' ), new ModelElement( 'caption' ) );
|
|
|
|
|
+ } );
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|