/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /* globals console, window, document */ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'; import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset'; import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview'; const byClassName = className => element => element.hasClass( className ); const getRandom = () => parseInt( Math.random() * 1000 ); let renderId = 0; const getNextRenderId = () => renderId++; function mapMeta( editor ) { return metaElement => { if ( metaElement.hasClass( 'box-meta-header' ) ) { const title = getChildren( editor, metaElement ) .filter( byClassName( 'box-meta-header-title' ) ) .pop().getChild( 0 ).getChild( 0 ).data; return { header: { title } }; } if ( metaElement.hasClass( 'box-meta-author' ) ) { const link = metaElement.getChild( 0 ); return { author: { name: link.getChild( 0 ).data, website: link.getAttribute( 'href' ) } }; } }; } function boxRefresh( model ) { const differ = model.document.differ; const changes = differ.getChanges(); console.log( `boxRefresh() size: ${ changes.length }` ); const boxElements = [ ...changes ] .filter( ( { type } ) => type === 'attribute' || type === 'insert' || type === 'remove' ) .map( ( { range, position } ) => range && range.start.nodeAfter || position && position.parent ) .filter( element => element && element.is( 'element', 'box' ) ); const boxToRefresh = new Set( boxElements ); console.group( 'refreshItem' ); [ ...boxToRefresh.values() ].forEach( box => differ.refreshItem( box ) ); console.groupEnd(); return boxToRefresh.size > 1; } function getChildren( editor, viewElement ) { return [ ...( editor.editing.view.createRangeIn( viewElement ) ) ] .filter( ( { type } ) => type === 'elementStart' ) .map( ( { item } ) => item ); } function getBoxUpcastConverter( editor ) { return dispatcher => dispatcher.on( 'element:div', ( event, data, conversionApi ) => { const viewElement = data.viewItem; const writer = conversionApi.writer; if ( !viewElement.hasClass( 'box' ) ) { return; } const box = writer.createElement( 'box' ); if ( !conversionApi.safeInsert( box, data.modelCursor ) ) { return; } const elements = getChildren( editor, viewElement ); const fields = elements.filter( byClassName( 'box-content-field' ) ); const metaElements = elements.filter( byClassName( 'box-meta' ) ); const meta = metaElements.map( mapMeta( editor ) ).reduce( ( prev, current ) => Object.assign( prev, current ), {} ); writer.setAttribute( 'meta', meta, box ); for ( const field of fields ) { const boxField = writer.createElement( 'boxField' ); conversionApi.safeInsert( boxField, writer.createPositionAt( box, field.index ) ); conversionApi.convertChildren( field, boxField ); } conversionApi.consumable.consume( viewElement, { name: true } ); elements.map( element => { conversionApi.consumable.consume( element, { name: true } ); } ); conversionApi.updateConversionResult( box, data ); } ); } function downcastBox( modelElement, conversionApi ) { console.log( 'downcastBox' ); const { writer } = conversionApi; const viewBox = writer.createContainerElement( 'div', { class: 'box' } ); conversionApi.mapper.bindElements( modelElement, viewBox ); const contentWrap = writer.createContainerElement( 'div', { class: 'box-content' } ); writer.insert( writer.createPositionAt( viewBox, 0 ), contentWrap ); for ( const [ meta, metaValue ] of Object.entries( modelElement.getAttribute( 'meta' ) ) ) { if ( meta === 'header' ) { const header = writer.createRawElement( 'div', { class: 'box-meta box-meta-header' }, function( domElement ) { domElement.innerHTML = `
`; } ); writer.insert( writer.createPositionBefore( contentWrap ), header ); } if ( meta === 'author' ) { const author = writer.createRawElement( 'div', { class: 'box-meta box-meta-author' }, domElement => { domElement.innerHTML = `${ metaValue.name }`; } ); writer.insert( writer.createPositionAfter( contentWrap ), author ); } } for ( const field of modelElement.getChildren() ) { const viewField = writer.createContainerElement( 'div', { class: 'box-content-field' } ); writer.insert( writer.createPositionAt( contentWrap, field.index ), viewField ); conversionApi.mapper.bindElements( field, viewField ); } // At this point we're inserting whole "component". Equivalent to (JSX-like notation): // // "rendered" view Mapping/source // //