/** * @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 ); 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 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 ) { 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' }, 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 ); conversionApi.consumable.consume( field, 'insert' ); // Might be simplified to: // // writer.defineSlot( field, viewField, field.index ); // // but would require a converter: // // editor.conversion.for( 'downcast' ).elementToElement( { // .slotToElement()? // model: 'viewField', // view: { name: 'div', class: 'box-content-field' } // } ); } // At this point we're inserting whole "component". Equivalent to (JSX-like notation): // // "rendered" view Mapping/source // //