| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537 |
- /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
- import {
- modelElementIsViewElement, modelAttributeIsViewElement, modelAttributeIsViewAttribute
- } from '../../src/conversion/definition-conversion';
- import Conversion from '../../src/conversion/conversion';
- import ViewConversionDispatcher from '../../src/conversion/viewconversiondispatcher';
- import { convertText, convertToModelFragment } from '../../src/conversion/view-to-model-converters';
- import EditingController from '../../src/controller/editingcontroller';
- import Model from '../../src/model/model';
- import ModelRange from '../../src/model/range';
- import { stringify as viewStringify, parse as viewParse } from '../../src/dev-utils/view';
- import { stringify as modelStringify } from '../../src/dev-utils/model';
- describe( 'definition-conversion', () => {
- let viewDispatcher, model, schema, conversion, modelRoot, viewRoot;
- beforeEach( () => {
- model = new Model();
- const controller = new EditingController( model );
- const modelDoc = model.document;
- modelRoot = modelDoc.createRoot();
- viewRoot = controller.view.getRoot();
- // Set name of view root the same as dom root.
- // This is a mock of attaching view root to dom root.
- viewRoot._name = 'div';
- schema = model.schema;
- schema.extend( '$text', {
- allowAttributes: [ 'bold' ]
- } );
- schema.register( 'paragraph', {
- inheritAllFrom: '$block'
- } );
- viewDispatcher = new ViewConversionDispatcher( model, { schema } );
- viewDispatcher.on( 'text', convertText() );
- viewDispatcher.on( 'element', convertToModelFragment(), { priority: 'lowest' } );
- viewDispatcher.on( 'documentFragment', convertToModelFragment(), { priority: 'lowest' } );
- conversion = new Conversion();
- conversion.register( 'view', [ viewDispatcher ] );
- conversion.register( 'model', [ controller.modelToView ] );
- } );
- describe( 'modelElementIsViewElement', () => {
- it( 'config.view is a string', () => {
- modelElementIsViewElement( conversion, { model: 'paragraph', view: 'p' } );
- test( '<p>Foo</p>', '<paragraph>Foo</paragraph>' );
- } );
- it( 'config.view is an object', () => {
- schema.register( 'fancyParagraph', {
- inheritAllFrom: 'paragraph'
- } );
- modelElementIsViewElement( conversion, {
- model: 'fancyParagraph',
- view: {
- name: 'p',
- class: 'fancy'
- }
- } );
- test( '<p class="fancy">Foo</p>', '<fancyParagraph>Foo</fancyParagraph>' );
- } );
- it( 'config.view is an object with upcastAlso defined', () => {
- modelElementIsViewElement( conversion, {
- model: 'paragraph',
- view: 'p',
- upcastAlso: [
- 'div',
- {
- // Match any name.
- name: /./,
- style: {
- display: 'block'
- }
- }
- ]
- } );
- test( '<p>Foo</p>', '<paragraph>Foo</paragraph>' );
- test( '<div>Foo</div>', '<paragraph>Foo</paragraph>', '<p>Foo</p>' );
- test( '<span style="display:block">Foo</span>', '<paragraph>Foo</paragraph>', '<p>Foo</p>' );
- } );
- it( 'upcastAlso given as a function', () => {
- schema.register( 'heading', {
- inheritAllFrom: '$block'
- } );
- modelElementIsViewElement( conversion, {
- model: 'heading',
- view: 'h2',
- upcastAlso: viewElement => {
- const fontSize = viewElement.getStyle( 'font-size' );
- if ( !fontSize ) {
- return null;
- }
- const match = fontSize.match( /(\d+)\s*px/ );
- if ( !match ) {
- return null;
- }
- const size = Number( match[ 1 ] );
- if ( size >= 26 ) {
- return { name: true, style: [ 'font-size' ] };
- }
- return null;
- }
- } );
- modelElementIsViewElement( conversion, {
- model: 'paragraph',
- view: 'p'
- } );
- test( '<p></p>', '<paragraph></paragraph>' );
- test( '<p style="font-size:20px"></p>', '<paragraph></paragraph>', '<p></p>' );
- test( '<h2></h2>', '<heading></heading>' );
- test( '<p style="font-size:26px"></p>', '<heading></heading>', '<h2></h2>' );
- } );
- } );
- describe( 'modelAttributeIsViewElement', () => {
- beforeEach( () => {
- modelElementIsViewElement( conversion, { model: 'paragraph', view: 'p' } );
- } );
- it( 'config.view is a string', () => {
- modelAttributeIsViewElement( conversion, 'bold', { view: 'strong' } );
- test( '<p><strong>Foo</strong> bar</p>', '<paragraph><$text bold="true">Foo</$text> bar</paragraph>' );
- } );
- it( 'config.view is an object', () => {
- modelAttributeIsViewElement( conversion, 'bold', {
- view: {
- name: 'span',
- class: 'bold'
- }
- } );
- test( '<p><span class="bold">Foo</span> bar</p>', '<paragraph><$text bold="true">Foo</$text> bar</paragraph>' );
- } );
- it( 'config.view is an object with upcastAlso defined', () => {
- modelAttributeIsViewElement( conversion, 'bold', {
- view: 'strong',
- upcastAlso: [
- 'b',
- {
- name: 'span',
- class: 'bold'
- },
- {
- name: 'span',
- style: {
- 'font-weight': 'bold'
- }
- },
- viewElement => {
- const fontWeight = viewElement.getStyle( 'font-weight' );
- if ( viewElement.is( 'span' ) && fontWeight && /\d+/.test( fontWeight ) && Number( fontWeight ) > 500 ) {
- return {
- name: true,
- style: [ 'font-weight' ]
- };
- }
- }
- ]
- } );
- test(
- '<p><strong>Foo</strong></p>',
- '<paragraph><$text bold="true">Foo</$text></paragraph>'
- );
- test(
- '<p><b>Foo</b></p>',
- '<paragraph><$text bold="true">Foo</$text></paragraph>',
- '<p><strong>Foo</strong></p>'
- );
- test(
- '<p><span class="bold">Foo</span></p>',
- '<paragraph><$text bold="true">Foo</$text></paragraph>',
- '<p><strong>Foo</strong></p>'
- );
- test(
- '<p><span style="font-weight: bold;">Foo</span></p>',
- '<paragraph><$text bold="true">Foo</$text></paragraph>',
- '<p><strong>Foo</strong></p>'
- );
- test(
- '<p><span style="font-weight: 500;">Foo</span></p>',
- '<paragraph>Foo</paragraph>',
- '<p>Foo</p>'
- );
- test(
- '<p><span style="font-weight: 600;">Foo</span></p>',
- '<paragraph><$text bold="true">Foo</$text></paragraph>',
- '<p><strong>Foo</strong></p>'
- );
- } );
- it( 'config.model is a string', () => {
- schema.extend( '$text', {
- allowAttributes: [ 'styled' ]
- } );
- modelAttributeIsViewElement( conversion, 'styled', {
- model: 'dark',
- view: {
- name: 'span',
- class: [ 'styled', 'styled-dark' ]
- }
- } );
- test( '<p><span class="styled styled-dark">Foo</span> bar</p>', '<paragraph><$text styled="dark">Foo</$text> bar</paragraph>' );
- } );
- it( 'config is an array', () => {
- schema.extend( '$text', {
- allowAttributes: [ 'fontSize' ]
- } );
- modelAttributeIsViewElement( conversion, 'fontSize', [
- {
- model: 'big',
- view: {
- name: 'span',
- style: {
- 'font-size': '1.2em'
- }
- }
- },
- {
- model: 'small',
- view: {
- name: 'span',
- style: {
- 'font-size': '0.8em'
- }
- }
- }
- ] );
- test(
- '<p><span style="font-size:1.2em">Foo</span> bar</p>',
- '<paragraph><$text fontSize="big">Foo</$text> bar</paragraph>'
- );
- test(
- '<p><span style="font-size:0.8em">Foo</span> bar</p>',
- '<paragraph><$text fontSize="small">Foo</$text> bar</paragraph>'
- );
- } );
- it( 'config is an array with upcastAlso defined', () => {
- schema.extend( '$text', {
- allowAttributes: [ 'fontSize' ]
- } );
- modelAttributeIsViewElement( conversion, 'fontSize', [
- {
- model: 'big',
- view: {
- name: 'span',
- style: {
- 'font-size': '1.2em'
- }
- },
- upcastAlso: viewElement => {
- const fontSize = viewElement.getStyle( 'font-size' );
- if ( !fontSize ) {
- return null;
- }
- const match = fontSize.match( /(\d+)\s*px/ );
- if ( !match ) {
- return null;
- }
- const size = Number( match[ 1 ] );
- if ( viewElement.is( 'span' ) && size > 10 ) {
- return { name: true, style: [ 'font-size' ] };
- }
- return null;
- }
- },
- {
- model: 'small',
- view: {
- name: 'span',
- style: {
- 'font-size': '0.8em'
- }
- },
- upcastAlso: viewElement => {
- const fontSize = viewElement.getStyle( 'font-size' );
- if ( !fontSize ) {
- return null;
- }
- const match = fontSize.match( /(\d+)\s*px/ );
- if ( !match ) {
- return null;
- }
- const size = Number( match[ 1 ] );
- if ( viewElement.is( 'span' ) && size < 10 ) {
- return { name: true, style: [ 'font-size' ] };
- }
- return null;
- }
- }
- ] );
- test(
- '<p><span style="font-size:1.2em">Foo</span> bar</p>',
- '<paragraph><$text fontSize="big">Foo</$text> bar</paragraph>'
- );
- test(
- '<p><span style="font-size:12px">Foo</span> bar</p>',
- '<paragraph><$text fontSize="big">Foo</$text> bar</paragraph>',
- '<p><span style="font-size:1.2em">Foo</span> bar</p>'
- );
- test(
- '<p><span style="font-size:0.8em">Foo</span> bar</p>',
- '<paragraph><$text fontSize="small">Foo</$text> bar</paragraph>'
- );
- test(
- '<p><span style="font-size:8px">Foo</span> bar</p>',
- '<paragraph><$text fontSize="small">Foo</$text> bar</paragraph>',
- '<p><span style="font-size:0.8em">Foo</span> bar</p>'
- );
- test(
- '<p><span style="font-size:10px">Foo</span> bar</p>',
- '<paragraph>Foo bar</paragraph>',
- '<p>Foo bar</p>'
- );
- } );
- } );
- describe( 'modelAttributeIsViewAttribute', () => {
- beforeEach( () => {
- modelElementIsViewElement( conversion, { model: 'image', view: 'img' } );
- schema.register( 'image', {
- inheritAllFrom: '$block',
- } );
- } );
- it( 'config is not set', () => {
- schema.extend( 'image', {
- allowAttributes: [ 'src' ]
- } );
- modelAttributeIsViewAttribute( conversion, 'src' );
- test( '<img src="foo.jpg"></img>', '<image src="foo.jpg"></image>' );
- } );
- it( 'config.view is a string', () => {
- schema.extend( 'image', {
- allowAttributes: [ 'source' ]
- } );
- modelAttributeIsViewAttribute( conversion, 'source', { view: 'src' } );
- test( '<img src="foo.jpg"></img>', '<image source="foo.jpg"></image>' );
- } );
- it( 'config.view is an object', () => {
- schema.extend( 'image', {
- allowAttributes: [ 'aside' ]
- } );
- modelAttributeIsViewAttribute( conversion, 'aside', {
- model: true,
- view: {
- name: 'img',
- key: 'class',
- value: 'aside half-size'
- }
- } );
- modelElementIsViewElement( conversion, { model: 'paragraph', view: 'p' } );
- test( '<img class="aside half-size"></img>', '<image aside="true"></image>' );
- test( '<p class="aside half-size"></p>', '<paragraph></paragraph>', '<p></p>' );
- } );
- it( 'config is an array', () => {
- schema.extend( 'image', {
- allowAttributes: [ 'styled' ]
- } );
- modelAttributeIsViewAttribute( conversion, 'styled', [
- {
- model: 'dark',
- view: {
- key: 'class',
- value: 'styled styled-dark'
- }
- },
- {
- model: 'light',
- view: {
- key: 'class',
- value: 'styled styled-light'
- }
- }
- ] );
- test( '<img class="styled styled-dark"></img>', '<image styled="dark"></image>' );
- test( '<img class="styled styled-light"></img>', '<image styled="light"></image>' );
- } );
- it( 'config is an array with upcastAlso defined', () => {
- modelElementIsViewElement( conversion, { model: 'paragraph', view: 'p' } );
- schema.extend( 'paragraph', {
- allowAttributes: [ 'align' ]
- } );
- modelAttributeIsViewAttribute( conversion, 'align', [
- {
- model: 'right',
- view: {
- key: 'class',
- value: 'align-right'
- },
- upcastAlso: viewElement => {
- if ( viewElement.getStyle( 'text-align' ) == 'right' ) {
- return {
- style: [ 'text-align' ]
- };
- }
- return null;
- }
- },
- {
- model: 'center',
- view: {
- key: 'class',
- value: 'align-center'
- },
- upcastAlso: {
- style: {
- 'text-align': 'center'
- }
- }
- }
- ] );
- test(
- '<p class="align-right">Foo</p>',
- '<paragraph align="right">Foo</paragraph>'
- );
- test(
- '<p style="text-align:right">Foo</p>',
- '<paragraph align="right">Foo</paragraph>',
- '<p class="align-right">Foo</p>'
- );
- test(
- '<p class="align-center">Foo</p>',
- '<paragraph align="center">Foo</paragraph>'
- );
- test(
- '<p style="text-align:center">Foo</p>',
- '<paragraph align="center">Foo</paragraph>',
- '<p class="align-center">Foo</p>'
- );
- } );
- } );
- function test( input, expectedModel, expectedView = null ) {
- loadData( input );
- expect( modelStringify( model.document.getRoot() ) ).to.equal( expectedModel );
- expect( viewStringify( viewRoot, null, { ignoreRoot: true } ) ).to.equal( expectedView || input );
- }
- function loadData( input ) {
- const parsedView = viewParse( input );
- const convertedModel = viewDispatcher.convert( parsedView );
- model.change( writer => {
- writer.remove( ModelRange.createFromParentsAndOffsets( modelRoot, 0, modelRoot, modelRoot.maxOffset ) );
- writer.insert( convertedModel, modelRoot, 0 );
- } );
- }
- } );
|