import { Plugin, Widget, toWidget, viewToModelPositionOutsideModelElement } from 'ckeditor5'; import FieldCommand from './fieldcommand'; String.prototype.rtrim = function (s) { if (s === undefined) s = '\\s'; return this.replace(new RegExp("[" + s + "]*$"), ''); }; String.prototype.ltrim = function (s) { if (s === undefined) s = '\\s'; return this.replace(new RegExp("^[" + s + "]*"), ''); }; export class FieldConfig{ fields = [ ] } export default class FieldEditing extends Plugin { static get requires() { return [ Widget ]; } init() { this._defineSchema(); this._defineConverters(); this.editor.commands.add( 'field', new FieldCommand( this.editor ) ); this.editor.editing.mapper.on( 'viewToModelPosition', viewToModelPositionOutsideModelElement( this.editor.model, viewElement => viewElement.hasClass( 'field' ) ) ); this.editor.config.define( 'fieldConfig', FieldConfig ); } _defineSchema() { const schema = this.editor.model.schema; schema.register( 'field', { // Behaves like a self-contained inline object (e.g. an inline image) // allowed in places where $text is allowed (e.g. in paragraphs). // The inline widget can have the same attributes as text (for example linkHref, bold). inheritAllFrom: '$inlineObject', // The field can have many types, like date, name, surname, etc: allowAttributes: [ 'field' ] } ); } _defineConverters() { const conversion = this.editor.conversion; conversion.for( 'upcast' ).elementToElement( { view: { name: 'span', classes: [ 'field' ] }, model: (viewElement, conversionApi) => { // Extract the "name" from "{name}". if (viewElement === undefined) { return null; } const { writer } = conversionApi; const name = viewElement.getChild(0)?._textData; const fieldtype = viewElement.getAttribute("fieldtype"); const guid = viewElement.getAttribute("guid"); const fieldid = viewElement.getAttribute("fieldid"); const field = { name, type: fieldtype, guid, fieldid }; return writer.createElement('field', { field }); } } ); conversion.for( 'editingDowncast' ).elementToElement( { model: 'field', view: ( modelItem, { writer: viewWriter } ) => { const widgetElement = createfieldView( modelItem, viewWriter ); // Enable widget handling on a field element inside the editing view. return toWidget( widgetElement, viewWriter ); } } ); conversion.for( 'dataDowncast' ).elementToElement( { model: 'field', view: ( modelItem, { writer: viewWriter } ) => createfieldView( modelItem, viewWriter ) } ); // Helper method for both downcast converters. // function createfieldView( modelItem : Element, viewWriter : DowncastWriter ) { function createfieldView( modelItem, viewWriter ) { const field = modelItem.getAttribute( 'field' ); //todo any is a cop out. const fieldView = viewWriter.createContainerElement( 'span', { class: 'field', fieldType: field.type, guid: field.guid, fieldid: (field.guid !== undefined) ? null : field.id } ); // Insert the field name (as a text). const innerText = viewWriter.createText( '{' + field.name.ltrim('{').rtrim('}') + '}' ); viewWriter.insert( viewWriter.createPositionAt( fieldView, 0 ), innerText ); return fieldView; } } }