Commit c1e82af5 authored by Daniel Kaufmann's avatar Daniel Kaufmann

[FEATURE] add custom plugins

parent 105eda9b
Pipeline #325 failed with stages
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -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