-
-
Save fredck/a80c0b86ed53d3da3776c55ebb3e0e19 to your computer and use it in GitHub Desktop.
Introduces `editor.data.view` and the "Live HTML Data Processor"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. | |
* For licensing, see LICENSE.md. | |
*/ | |
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | |
import View from '@ckeditor/ckeditor5-engine/src/view/view'; | |
import ContainerElement from '@ckeditor/ckeditor5-engine/src/view/containerelement'; | |
import RootEditableElement from '@ckeditor/ckeditor5-engine/src/view/rooteditableelement'; | |
import ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment'; | |
import { | |
clearAttributes, convertCollapsedSelection, | |
convertRangeSelection, | |
remove | |
} from '@ckeditor/ckeditor5-engine/src/conversion/downcasthelpers'; | |
export default class DataView extends Plugin { | |
constructor( editor ) { | |
super( editor ); | |
const data = editor.data; | |
/** | |
* Data view controller. | |
* | |
* @readonly | |
* @memberOf Editor | |
* @member {View} #view | |
*/ | |
const view = data.view = new View( data.stylesProcessor ); | |
// Minor improvements that don't hurt. | |
{ | |
// We don't need observers in this view. | |
view._observers = new Map(); | |
// We don't care about the selection. | |
view.stopListening( view.document.selection, 'change' ); | |
} | |
// Destroy the view when the data controller is destroyed. | |
{ | |
data.decorate( 'destroy' ); | |
data.once( 'destroy', () => view.destroy() ); | |
} | |
// View updating. It's highly inspired by @ckeditor/ckeditor5-engine/src/controller/editingcontroller. | |
{ | |
const model = data.model; | |
const downcastDispatcher = data.downcastDispatcher; | |
const { document, markers } = model; | |
// Disable rendering while model changes are happening (they could happen during rendering as well). | |
this.listenTo( model, '_beforeChanges', () => view._disableRendering( true ), { priority: 'highest' } ); | |
this.listenTo( model, '_afterChanges', () => view._disableRendering( false ), { priority: 'lowest' } ); | |
// Whenever the model document is changed, convert those changes to the view. | |
// Do it on 'low' priority, so changes are converted after other listeners did their job. | |
this.listenTo( document, 'change:data', () => { | |
view.change( writer => { | |
downcastDispatcher.convertChanges( document.differ, markers, writer ); | |
} ); | |
}, { priority: 'low' } ); | |
downcastDispatcher.on( 'remove', remove(), { priority: 'low' } ); | |
// Binds {@link module:engine/view/document~Document#roots view roots collection} to | |
// {@link module:engine/model/document~Document#roots model roots collection} so creating | |
// model root automatically creates corresponding view root. | |
view.document.roots.bindTo( model.document.roots ).using( root => { | |
// $graveyard is a special root that has no reflection in the view. | |
if ( root.rootName === '$graveyard' ) { | |
return null; | |
} | |
const viewRoot = new DataViewRootElement( view.document, root.name ); | |
viewRoot.rootName = root.rootName; | |
data.mapper.bindElements( root, viewRoot ); | |
return viewRoot; | |
} ); | |
} | |
// Override data.stringify() as we now made the view direcly manipulable. | |
{ | |
const originalStringify = data.stringify; | |
data.stringify = modelElementOrFragment => { | |
if ( data.processor.rootToData && modelElementOrFragment.is( 'rootElement' ) ) { | |
const stringified = data.processor.rootToData( modelElementOrFragment.rootName ); | |
if ( stringified !== false ) { | |
return stringified; | |
} | |
} | |
return originalStringify.call( this, modelElementOrFragment ); | |
}; | |
} | |
} | |
} | |
/** | |
* A view root element. It targets the Data Controller, where no editing is expected. | |
* | |
* It emulates RootEditableElement, expect that it based on ContainerElement instead of EditableElement. | |
*/ | |
export class DataViewRootElement extends ContainerElement { | |
constructor( document, name ) { | |
super( document, name ); | |
this.rootName = 'main'; | |
} | |
get isReadOnly() { | |
return true; | |
} | |
} | |
// Methods and properties we want to copy from RootEditableElement. | |
[ 'is', 'rootName', '_name' ].forEach( property => { | |
Object.defineProperty( DataViewRootElement.prototype, property, | |
Object.getOwnPropertyDescriptor( RootEditableElement.prototype, property ) ); | |
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. | |
* For licensing, see LICENSE.md. | |
*/ | |
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | |
import DataView from './dataview'; | |
import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor'; | |
export default class LiveHtmlDataProcessor extends Plugin { | |
static get requires() { | |
return [ DataView ]; | |
} | |
static get pluginName() { | |
return 'LiveHtmlDataProcessor'; | |
} | |
constructor( editor ) { | |
super( editor ); | |
this.element = document.createElement( 'div' ); | |
editor.data.view.attachDomRoot( this.element ); | |
} | |
rootToData( rootName ) { | |
const element = this.editor.data.view.domRoots.get( rootName ); | |
if ( element && element === this.element ) { | |
// Here goes the performance boost -> getData() = element.innerHTML | |
return element.innerHTML; | |
} | |
return false; | |
} | |
} | |
// Methods and properties we want to copy from RootEditableElement. | |
[ 'toData', 'toView' ].forEach( property => { | |
Object.defineProperty( LiveHtmlDataProcessor.prototype, property, | |
Object.getOwnPropertyDescriptor( HtmlDataProcessor.prototype, property ) ); | |
} ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment