8
0
Просмотр исходного кода

Added DataProcessor, HtmlDataProcessor and BasicHtmlWriter classes.

Szymon Kupś 10 лет назад
Родитель
Сommit
52337a482c

+ 28 - 0
packages/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js

@@ -0,0 +1,28 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+/**
+ * BasicHtmlWriter class.
+ * Basic HTML writer, it uses native `innerHTML` property for basic conversion
+ * from DocumentFragment to HTML string.
+ *
+ * @class dataProcessor.BasicHtmlWriter
+ */
+export default class BasicHtmlWriter {
+	/**
+	 * Returns HTML string created from DocumentFragment.
+	 *
+	 * @param {DocumentFragment} fragment
+	 * @returns {String}
+	 */
+	getHtml( fragment ) {
+		const container = document.createElement( 'div' );
+		container.appendChild( fragment );
+
+		return container.innerHTML;
+	}
+}

+ 52 - 0
packages/ckeditor5-engine/src/dataprocessor/dataprocessor.js

@@ -0,0 +1,52 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import Config from '../config.js';
+
+/**
+ * Basic DataProcessor class. This class should be extended by actual DataProcessor implementations.
+ *
+ * @class dataProcessor.DataProcessor
+ */
+export default class DataProcessor {
+	/**
+	 * Creates a new instance of DataProcessor class.
+	 *
+	 *
+	 * @param {Object} config DataProcessor's configuration.
+	 * @constructor
+	 */
+	constructor( config ) {
+		/**
+		 * Holds configurations specific to this DataProcessor instance.
+		 *
+		 * @readonly
+		 * @property {Config}
+		 */
+		this.config = new Config( config );
+	}
+
+	/**
+	 * Converts DocumentFragment to output data supported by DataProcessor.
+	 *
+	 * @param {DocumentFragment} fragment
+	 * @returns {*}
+	 */
+	toData( fragment ) {
+		/*jshint unused:false*/
+	}
+
+	/**
+	 * Converts data supported by DataProcessor to DocumentFragment.
+	 *
+	 * @param {*} data
+	 * @returns {DocumentFragment}
+	 */
+	toDom( data ) {
+		/*jshint unused:false*/
+	}
+}

+ 74 - 0
packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js

@@ -0,0 +1,74 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import DataProcessor from './dataprocessor.js';
+import BasicHtmlWriter from './basichtmlwriter.js';
+
+/**
+ * HtmlDataProcessor class.
+ * This data processor implementation uses HTML as input/output data.
+ *
+ * @class dataProcessor.HtmlDataProcessor
+ * @extends dataProcessor.DataProcessor
+ */
+export default class HtmlDataProcessor extends DataProcessor {
+	/**
+	 * Creates a new instance of the HtmlDataProcessor class.
+	 *
+	 *
+	 * @param {Object} config HtmlDataProcessor's configuration.
+	 * @constructor
+	 */
+	constructor( config ) {
+		super( config );
+
+		/**
+		 * DOMParser instance used to parse HTML string to HTMLDocument.
+		 *
+		 * @private
+		 * @property {DOMParser}
+		 */
+		this._domParser = new DOMParser();
+
+		/**
+		 * BasicHtmlWriter instance used to convert DOM elements to HTML string.
+		 *
+		 * @private
+		 * @type {dataProcessor.BasicHtmlWriter}
+		 */
+		this._htmlWriter = new BasicHtmlWriter();
+	}
+
+	/**
+	 * Converts provided document fragment to data format - in this case HTML string.
+	 *
+	 * @param {DocumentFragment} fragment
+	 * @returns {String}
+	 */
+	toData( fragment ) {
+		return this._htmlWriter.getHtml( fragment );
+	}
+
+	/**
+	 * Converts HTML String to its DOM representation. Returns DocumentFragment, containing nodes parsed from
+	 * provided data.
+	 *
+	 * @param {String} data
+	 * @returns {DocumentFragment}
+	 */
+	toDom( data ) {
+		const document = this._domParser.parseFromString( data, 'text/html' );
+		const fragment = document.createDocumentFragment();
+		const nodes = document.body.childNodes;
+
+		while ( nodes.length > 0 ) {
+			fragment.appendChild( nodes [0] );
+		}
+
+		return fragment;
+	}
+}

+ 34 - 0
packages/ckeditor5-engine/tests/dataprocessor/dataprocessor.js

@@ -0,0 +1,34 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import DataProcessor from '/ckeditor5/core/dataprocessor/dataprocessor.js';
+
+describe( 'DataProcessor', () => {
+	const config = {
+		option: 'test'
+	};
+	const dataProcessor = new DataProcessor( config );
+
+	describe( 'constructor', () => {
+		it( 'should set the `config` property', () => {
+			expect( dataProcessor ).to.have.property( 'config' );
+			expect( dataProcessor.config.get( 'option' ) ).to.equal( 'test' );
+		} );
+	} );
+
+	describe( 'toData', () => {
+		it( 'should be defined', () => {
+			expect( dataProcessor.toData ).to.be.function;
+		} );
+	} );
+
+	describe( 'toDom', () => {
+		it( 'should be defined', () => {
+			expect( dataProcessor.toDom ).to.be.function;
+		} );
+	} );
+} );

+ 121 - 0
packages/ckeditor5-engine/tests/dataprocessor/htmldataprocessor.js

@@ -0,0 +1,121 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import HtmlDataProcessor from '/ckeditor5/core/dataprocessor/htmldataprocessor.js';
+
+describe( 'HtmlDataProcessor', () => {
+	const dataProcessor = new HtmlDataProcessor();
+
+	describe( 'toDom', () => {
+		it( 'should return empty DocumentFragment when empty string is passed', () => {
+			const fragment = dataProcessor.toDom( '' );
+			expect( fragment ).to.be.an.instanceOf( DocumentFragment );
+			expect( fragment.childNodes.length ).to.equal( 0 );
+		} );
+
+		it( 'should convert HTML to DocumentFragment with single text node', () => {
+			const fragment = dataProcessor.toDom( 'foo bar' );
+			expect( fragment.childNodes.length ).to.equal( 1 );
+			expect( fragment.childNodes[ 0 ].nodeType ).to.equal( Node.TEXT_NODE );
+			expect( fragment.childNodes[ 0 ].textContent ).to.equal( 'foo bar' );
+		} );
+
+		it( 'should convert HTML to DocumentFragment with multiple child nodes', () => {
+			const fragment = dataProcessor.toDom( '<p>foo</p><p>bar</p>' );
+			expect( fragment.childNodes.length ).to.equal( 2 );
+			expect( fragment.childNodes[ 0 ].nodeType ).to.equal( Node.ELEMENT_NODE );
+			expect( fragment.childNodes[ 0 ].textContent ).to.equal( 'foo' );
+			expect( fragment.childNodes[ 1 ].nodeType ).to.equal( Node.ELEMENT_NODE );
+			expect( fragment.childNodes[ 1 ].textContent ).to.equal( 'bar' );
+		} );
+
+		it( 'should return only elements inside body tag', () => {
+			const fragment = dataProcessor.toDom( '<html><head></head><body><p>foo</p></body></html>' );
+			expect( fragment.childNodes.length ).to.equal( 1 );
+			expect( fragment.childNodes[ 0 ].textContent ).to.equal( 'foo' );
+			expect( fragment.childNodes[ 0 ].tagName.toLowerCase() ).to.equal( 'p' );
+		} );
+
+		it( 'should not add any additional nodes', () => {
+			const fragment = dataProcessor.toDom( 'foo <b>bar</b> text' );
+			expect( fragment.childNodes.length ).to.equal( 3 );
+			expect( fragment.childNodes[ 0 ].nodeType ).to.equal( Node.TEXT_NODE );
+			expect( fragment.childNodes[ 0 ].textContent ).to.equal( 'foo ' );
+			expect( fragment.childNodes[ 1 ].nodeType ).to.equal( Node.ELEMENT_NODE );
+			expect( fragment.childNodes[ 1 ].innerHTML ).to.equal( 'bar' );
+			expect( fragment.childNodes[ 2 ].nodeType ).to.equal( Node.TEXT_NODE );
+			expect( fragment.childNodes[ 2 ].textContent ).to.equal( ' text' );
+		} );
+
+		it( 'should parse element attributes', () => {
+			const fragment = dataProcessor.toDom( '<p class="paragraph" data-id="12"></p>' );
+			expect( fragment.childNodes.length ).to.equal( 1 );
+			const childNode = fragment.childNodes[ 0 ];
+
+			expect( childNode.attributes.length ).to.equal( 2 );
+			expect( childNode.getAttribute( 'class' ) ).to.equal( 'paragraph' );
+			expect( childNode.getAttribute( 'data-id' ) ).to.equal( '12' );
+		} );
+	} );
+
+	describe( 'toData', () => {
+		it( 'should return empty string when empty DocumentFragment is passed' , () => {
+			const data = dataProcessor.toData( document.createDocumentFragment() );
+			expect( data ).to.equal( '' );
+		} );
+
+		it( 'should create text from single text node', () => {
+			const text = 'foo bar';
+			const fragment = document.createDocumentFragment();
+			const textNode = document.createTextNode( text );
+			fragment.appendChild( textNode );
+
+			const data = dataProcessor.toData( fragment );
+			expect( data ).to.equal( text );
+		} );
+
+		it( 'should return correct HTML from fragment with paragraph', () => {
+			const fragment = document.createDocumentFragment();
+			const paragraph = document.createElement( 'p' );
+			paragraph.textContent = 'foo bar';
+			fragment.appendChild( paragraph );
+
+			const data = dataProcessor.toData( fragment );
+			expect( data ).to.equal( '<p>foo bar</p>' );
+		} );
+
+		it( 'should return correct HTML from fragment with multiple child nodes', () => {
+			const fragment = document.createDocumentFragment();
+			const text = document.createTextNode( 'foo bar' );
+			const paragraph = document.createElement( 'p' );
+			const div = document.createElement( 'div' );
+
+			paragraph.textContent = 'foo';
+			div.textContent = 'bar';
+
+			fragment.appendChild( text );
+			fragment.appendChild( paragraph );
+			fragment.appendChild( div );
+
+			const data = dataProcessor.toData( fragment );
+
+			expect( data ).to.equal( 'foo bar<p>foo</p><div>bar</div>' );
+		} );
+
+		it( 'should return HTML with attributes', () => {
+			const fragment = document.createDocumentFragment();
+			const paragraph = document.createElement( 'p' );
+			paragraph.setAttribute( 'class', 'paragraph' );
+			paragraph.setAttribute( 'data-id', '12' );
+			fragment.appendChild( paragraph );
+
+			const data = dataProcessor.toData( fragment );
+
+			expect( data ).to.equal( '<p class="paragraph" data-id="12"></p>' );
+		} );
+	} );
+} );