/** * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ import { downcastElementToElement, downcastAttributeToElement, downcastAttributeToAttribute, downcastMarkerToElement, downcastMarkerToHighlight } from '../../src/conversion/downcast-helpers'; import Conversion from '../../src/conversion/conversion'; import EditingController from '../../src/controller/editingcontroller'; import Model from '../../src/model/model'; import ModelRange from '../../src/model/range'; import ViewContainerElement from '../../src/view/containerelement'; import ViewAttributeElement from '../../src/view/attributeelement'; import ViewUIElement from '../../src/view/uielement'; import { stringify } from '../../src/dev-utils/view'; describe( 'downcast-helpers', () => { let conversion, model, modelRoot, viewRoot; beforeEach( () => { model = new Model(); const modelDoc = model.document; modelRoot = modelDoc.createRoot(); const controller = new EditingController( model ); // Set name of view root the same as dom root. // This is a mock of attaching view root to dom root. controller.view.getRoot()._name = 'div'; viewRoot = controller.view.getRoot(); conversion = new Conversion(); conversion.register( 'downcast', [ controller.downcastDispatcher ] ); } ); describe( 'downcastElementToElement', () => { it( 'config.view is a string', () => { const helper = downcastElementToElement( { model: 'paragraph', view: 'p' } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'paragraph', modelRoot, 0 ); } ); expectResult( '

' ); } ); it( 'can be overwritten using priority', () => { const helperA = downcastElementToElement( { model: 'paragraph', view: 'p' } ); const helperB = downcastElementToElement( { model: 'paragraph', view: 'foo' }, 'high' ); conversion.for( 'downcast' ).add( helperA ).add( helperB ); model.change( writer => { writer.insertElement( 'paragraph', modelRoot, 0 ); } ); expectResult( '' ); } ); it( 'config.view is an element instance', () => { const helper = downcastElementToElement( { model: 'paragraph', view: new ViewContainerElement( 'p' ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'paragraph', modelRoot, 0 ); } ); expectResult( '

' ); } ); it( 'config.view is a view element definition', () => { const helper = downcastElementToElement( { model: 'fancyParagraph', view: { name: 'p', class: 'fancy' } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'fancyParagraph', modelRoot, 0 ); } ); expectResult( '

' ); } ); it( 'config.view is a function', () => { const helper = downcastElementToElement( { model: 'heading', view: modelElement => new ViewContainerElement( 'h' + modelElement.getAttribute( 'level' ) ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'heading', { level: 2 }, modelRoot, 0 ); } ); expectResult( '

' ); } ); } ); describe( 'downcastAttributeToElement', () => { it( 'config.view is a string', () => { const helper = downcastAttributeToElement( 'bold', { view: 'strong' } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { bold: true }, modelRoot, 0 ); } ); expectResult( 'foo' ); } ); it( 'can be overwritten using priority', () => { const helperA = downcastAttributeToElement( 'bold', { view: 'strong' } ); const helperB = downcastAttributeToElement( 'bold', { view: 'b' }, 'high' ); conversion.for( 'downcast' ).add( helperA ).add( helperB ); model.change( writer => { writer.insertText( 'foo', { bold: true }, modelRoot, 0 ); } ); expectResult( 'foo' ); } ); it( 'config.view is an element instance', () => { const helper = downcastAttributeToElement( 'bold', { view: new ViewAttributeElement( 'strong' ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { bold: true }, modelRoot, 0 ); } ); expectResult( 'foo' ); } ); it( 'config.view is a view element definition', () => { const helper = downcastAttributeToElement( 'bold', { view: { name: 'span', class: 'bold' } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { bold: true }, modelRoot, 0 ); } ); expectResult( 'foo' ); } ); it( 'config.view is a view element definition, model attribute value specified', () => { const helper = downcastAttributeToElement( 'styled', { model: 'dark', view: { name: 'span', class: [ 'styled', 'styled-dark' ] } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { styled: 'dark' }, modelRoot, 0 ); } ); expectResult( 'foo' ); model.change( writer => { writer.setAttribute( 'styled', 'xyz', modelRoot.getChild( 0 ) ); } ); expectResult( 'foo' ); } ); it( 'multiple config items', () => { const helper = downcastAttributeToElement( 'fontSize', [ { model: 'big', view: { name: 'span', style: { 'font-size': '1.2em' } } }, { model: 'small', view: { name: 'span', style: { 'font-size': '0.8em' } } } ] ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { fontSize: 'big' }, modelRoot, 0 ); } ); expectResult( 'foo' ); model.change( writer => { writer.setAttribute( 'fontSize', 'small', modelRoot.getChild( 0 ) ); } ); expectResult( 'foo' ); model.change( writer => { writer.removeAttribute( 'fontSize', modelRoot.getChild( 0 ) ); } ); expectResult( 'foo' ); } ); it( 'config.view is a function', () => { const helper = downcastAttributeToElement( 'bold', { view: attributeValue => new ViewAttributeElement( 'span', { style: 'font-weight:' + attributeValue } ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', { bold: '500' }, modelRoot, 0 ); } ); expectResult( 'foo' ); } ); } ); describe( 'downcastAttributeToAttribute', () => { beforeEach( () => { conversion.for( 'downcast' ).add( downcastElementToElement( { model: 'image', view: 'img' } ) ); } ); it( 'config not set', () => { const helper = downcastAttributeToAttribute( 'src' ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { src: 'foo.jpg' }, modelRoot, 0 ); } ); expectResult( '' ); } ); it( 'config.view is a string', () => { const helper = downcastAttributeToAttribute( 'source', { view: 'src' } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { source: 'foo.jpg' }, modelRoot, 0 ); } ); expectResult( '' ); } ); it( 'can be overwritten using priority', () => { const helperA = downcastAttributeToAttribute( 'source', { view: 'href' } ); const helperB = downcastAttributeToAttribute( 'source', { view: 'src' }, 'high' ); conversion.for( 'downcast' ).add( helperA ).add( helperB ); model.change( writer => { writer.insertElement( 'image', { source: 'foo.jpg' }, modelRoot, 0 ); } ); expectResult( '' ); } ); it( 'config.view is an object', () => { const helper = downcastAttributeToAttribute( 'stylish', { view: { key: 'class', value: 'styled' } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { stylish: true }, modelRoot, 0 ); } ); expectResult( '' ); } ); it( 'config.view is an object, model attribute value specified', () => { const helper = downcastAttributeToAttribute( 'styled', { model: 'dark', view: { key: 'class', value: 'styled-dark styled' } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { styled: 'dark' }, modelRoot, 0 ); } ); expectResult( '' ); model.change( writer => { writer.setAttribute( 'styled', 'xyz', modelRoot.getChild( 0 ) ); } ); expectResult( '' ); } ); it( 'multiple config items', () => { const helper = downcastAttributeToAttribute( 'styled', [ { model: 'dark', view: { key: 'class', value: 'styled-dark' } }, { model: 'light', view: { key: 'class', value: 'styled-light' } } ] ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { styled: 'dark' }, modelRoot, 0 ); } ); expectResult( '' ); model.change( writer => { writer.setAttribute( 'styled', 'light', modelRoot.getChild( 0 ) ); } ); expectResult( '' ); model.change( writer => { writer.setAttribute( 'styled', 'xyz', modelRoot.getChild( 0 ) ); } ); expectResult( '' ); } ); it( 'config.view is a function', () => { const helper = downcastAttributeToAttribute( 'styled', { view: attributeValue => ( { key: 'class', value: 'styled-' + attributeValue } ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertElement( 'image', { styled: 'pull-out' }, modelRoot, 0 ); } ); expectResult( '' ); } ); } ); describe( 'downcastMarkerToElement', () => { it( 'config.view is a string', () => { const helper = downcastMarkerToElement( { model: 'search', view: 'marker-search' } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'search', ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 2 ) ); } ); expectResult( 'foo' ); } ); it( 'can be overwritten using priority', () => { const helperA = downcastMarkerToElement( { model: 'search', view: 'marker-search' } ); const helperB = downcastMarkerToElement( { model: 'search', view: 'search' }, 'high' ); conversion.for( 'downcast' ).add( helperA ).add( helperB ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'search', ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 2 ) ); } ); expectResult( 'foo' ); } ); it( 'config.view is an element instance', () => { const helper = downcastMarkerToElement( { model: 'search', view: new ViewUIElement( 'span', { 'data-marker': 'search' } ) } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'search', ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 2 ) ); } ); expectResult( 'foo' ); } ); it( 'config.view is a view element definition', () => { const helper = downcastMarkerToElement( { model: 'search', view: { name: 'span', attribute: { 'data-marker': 'search' } } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'search', ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 2 ) ); } ); expectResult( 'foo' ); } ); it( 'config.view is a function', () => { const helper = downcastMarkerToElement( { model: 'search', view: data => { return new ViewUIElement( 'span', { 'data-marker': 'search', 'data-start': data.isOpening } ); } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'search', ModelRange.createFromParentsAndOffsets( modelRoot, 1, modelRoot, 2 ) ); } ); expectResult( 'foo' ); } ); } ); describe( 'downcastMarkerToHighlight', () => { it( 'config.view is a highlight descriptor', () => { const helper = downcastMarkerToHighlight( { model: 'comment', view: { class: 'comment' } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'comment', ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, 3 ) ); } ); expectResult( 'foo' ); } ); it( 'can be overwritten using priority', () => { const helperA = downcastMarkerToHighlight( { model: 'comment', view: { class: 'comment' } } ); const helperB = downcastMarkerToHighlight( { model: 'comment', view: { class: 'new-comment' } }, 'high' ); conversion.for( 'downcast' ).add( helperA ).add( helperB ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'comment', ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, 3 ) ); } ); expectResult( 'foo' ); } ); it( 'config.view is a function', () => { const helper = downcastMarkerToHighlight( { model: 'comment', view: data => { const commentType = data.markerName.split( ':' )[ 1 ]; return { class: [ 'comment', 'comment-' + commentType ] }; } } ); conversion.for( 'downcast' ).add( helper ); model.change( writer => { writer.insertText( 'foo', modelRoot, 0 ); writer.setMarker( 'comment:abc', ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, 3 ) ); } ); expectResult( 'foo' ); } ); } ); function expectResult( string ) { expect( stringify( viewRoot, null, { ignoreRoot: true } ) ).to.equal( string ); } } );