Commit c1e82af5 authored by Daniel Kaufmann's avatar Daniel Kaufmann

[FEATURE] add custom plugins

parent 105eda9b
Pipeline #325 failed with stages
This diff is collapsed.
This diff is collapsed.
...@@ -37,6 +37,8 @@ import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefrom ...@@ -37,6 +37,8 @@ import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefrom
import Table from '@ckeditor/ckeditor5-table/src/table'; import Table from '@ckeditor/ckeditor5-table/src/table';
import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar'; import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
import TextTransformation from '@ckeditor/ckeditor5-typing/src/texttransformation'; import TextTransformation from '@ckeditor/ckeditor5-typing/src/texttransformation';
import {KeepBrTags} from './keepbrtags';
import {KeepCustomClasses} from './keepcustomclasses';
export default class DecoupledEditor extends DecoupledEditorBase {} export default class DecoupledEditor extends DecoupledEditorBase {}
...@@ -72,7 +74,9 @@ DecoupledEditor.builtinPlugins = [ ...@@ -72,7 +74,9 @@ DecoupledEditor.builtinPlugins = [
PasteFromOffice, PasteFromOffice,
Table, Table,
TableToolbar, TableToolbar,
TextTransformation TextTransformation,
KeepBrTags,
KeepCustomClasses
]; ];
// Editor configuration. // Editor configuration.
......
import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
export class KeepBrTags extends Plugin {
init() {
const editor = this.editor;
editor.model.schema.register( 'br', {
allowWhere: '$block',
allowContentOf: '$root'
});
editor.model.schema.addAttributeCheck( context => {
if ( context.endsWith( 'br' ) ) {
return true;
}
} );
editor.conversion.for( 'upcast' ).elementToElement( {
view: 'br',
model: ( viewElement, modelWriter ) => {
return modelWriter.createElement( 'br', viewElement.getAttributes() );
}
} );
editor.conversion.for( 'downcast' ).elementToElement( {
model: 'br',
view: 'br'
} );
}
}
\ No newline at end of file
import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
export class KeepCustomClasses extends Plugin {
init() {
const editor = this.editor;
// Define on which elements the CSS classes should be preserved:
setupCustomClassConversion( 'h2', 'heading1', editor );
setupCustomClassConversion( 'p', 'paragraph', editor );
// Define custom attributes that should be preserved.
setupCustomAttributeConversion( 'h2', 'heading1', 'id', editor );
setupCustomAttributeConversion( 'p', 'paragraph', 'id', editor );
function setupCustomClassConversion( viewElementName, modelElementName, editor ) {
// The 'customClass' attribute will store custom classes from the data in the model so schema definitions allow this attribute.
editor.model.schema.extend( modelElementName, { allowAttributes: [ 'customClass' ] } );
// Define upcast converters for the <img> and <table> elements with a "low" priority so they are run after the default converters.
editor.conversion.for( 'upcast' ).add( upcastCustomClasses( viewElementName ), { priority: 'low' } );
// Define downcast converters for a model element with a "low" priority so they are run after the default converters.
// Use `downcastCustomClassesToFigure` if you'd like to keep your classes on <figure> element or `downcastCustomClassesToChild` if you'd like to keep your classes on a <figure> child element, i.e. <img>.
editor.conversion.for( 'downcast' ).add( downcastCustomClassesToFigure( modelElementName ), { priority: 'low' } );
// editor.conversion.for( 'downcast' ).add( downcastCustomClassesToChild( viewElementName, modelElementName ), { priority: 'low' } );
}
function setupCustomAttributeConversion( viewElementName, modelElementName, viewAttribute, editor ) {
// Extend the schema to store an attribute in the model.
const modelAttribute = `custom${ viewAttribute }`;
editor.model.schema.extend( modelElementName, { allowAttributes: [ modelAttribute ] } );
editor.conversion.for( 'upcast' ).add( upcastAttribute( viewElementName, viewAttribute, modelAttribute ) );
editor.conversion.for( 'downcast' ).add( downcastAttribute( modelElementName, viewElementName, viewAttribute, modelAttribute ) );
}
function upcastCustomClasses( elementName ) {
return dispatcher => dispatcher.on( `element:${ elementName }`, ( evt, data, conversionApi ) => {
const viewItem = data.viewItem;
const modelRange = data.modelRange;
const modelElement = modelRange && modelRange.start.nodeAfter;
if ( !modelElement ) {
return;
}
// The upcast conversion picks up classes from the base element and from the <figure> element so it should be extensible.
const currentAttributeValue = modelElement.getAttribute( 'customClass' ) || [];
currentAttributeValue.push( ...viewItem.getClassNames() );
conversionApi.writer.setAttribute( 'customClass', currentAttributeValue, modelElement );
} );
}
function downcastCustomClassesToFigure( modelElementName ) {
return dispatcher => dispatcher.on( `insert:${ modelElementName }`, ( evt, data, conversionApi ) => {
const modelElement = data.item;
const viewFigure = conversionApi.mapper.toViewElement( modelElement );
if ( !viewFigure ) {
return;
}
// The code below assumes that classes are set on the <figure> element.
conversionApi.writer.addClass( modelElement.getAttribute( 'customClass' ), viewFigure );
} );
}
/**
* Helper method that searches for a given view element in all children of the model element.
*
* @param {module:engine/view/item~Item} viewElement
* @param {String} viewElementName
* @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
* @return {module:engine/view/item~Item}
*/
function findViewChild( viewElement, viewElementName, conversionApi ) {
const viewChildren = Array.from( conversionApi.writer.createRangeIn( viewElement ).getItems() );
return viewChildren.find( item => item.is( viewElementName ) );
}
/**
* Returns the custom attribute upcast converter.
*/
function upcastAttribute( viewElementName, viewAttribute, modelAttribute ) {
return dispatcher => dispatcher.on( `element:${ viewElementName }`, ( evt, data, conversionApi ) => {
const viewItem = data.viewItem;
const modelRange = data.modelRange;
const modelElement = modelRange && modelRange.start.nodeAfter;
if ( !modelElement ) {
return;
}
conversionApi.writer.setAttribute( modelAttribute, viewItem.getAttribute( viewAttribute ), modelElement );
} );
}
/**
* Returns the custom attribute downcast converter.
*/
function downcastAttribute( modelElementName, viewElementName, viewAttribute, modelAttribute ) {
return dispatcher => dispatcher.on( `insert:${ modelElementName }`, ( evt, data, conversionApi ) => {
const modelElement = data.item;
const viewFigure = conversionApi.mapper.toViewElement( modelElement );
const viewElement = findViewChild( viewFigure, viewElementName, conversionApi );
if ( !viewElement ) {
return;
}
conversionApi.writer.setAttribute( viewAttribute, modelElement.getAttribute( modelAttribute ), viewElement );
} );
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment