8
0
فهرست منبع

Merge pull request #498 from ckeditor/t/475

t/475 Wrapping text into attribute causes error.
Piotrek Koszuliński 9 سال پیش
والد
کامیت
5b9c67403d

+ 2 - 2
packages/ckeditor5-engine/src/conversion/model-to-view-converters.js

@@ -212,7 +212,7 @@ export function wrap( elementCreator ) {
 	return ( evt, data, consumable, conversionApi ) => {
 		consumable.consume( data.item, eventNameToConsumableType( evt.name ) );
 
-		const viewRange = conversionApi.mapper.toViewRange( data.range );
+		let viewRange = conversionApi.mapper.toViewRange( data.range );
 
 		const viewElement = ( elementCreator instanceof ViewElement ) ?
 			elementCreator.clone( true ) :
@@ -223,7 +223,7 @@ export function wrap( elementCreator ) {
 		// view element basing on old value and unwrap it before wrapping with a newly created view element.
 		if ( data.attributeOldValue !== null && !( elementCreator instanceof ViewElement ) ) {
 			const oldViewElement = elementCreator( data.attributeOldValue, data, consumable, conversionApi );
-			conversionApi.writer.unwrap( viewRange, oldViewElement, evt.priority );
+			viewRange = conversionApi.writer.unwrap( viewRange, oldViewElement, evt.priority );
 		}
 
 		conversionApi.writer.wrap( viewRange, viewElement, evt.priority );

+ 34 - 0
packages/ckeditor5-engine/tests/conversion/model-to-view-converters.js

@@ -243,6 +243,40 @@ describe( 'wrap/unwrap', () => {
 
 		expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p></div>' );
 	} );
+
+	it( 'should update range on re-wrapping attribute (#475)', () => {
+		const modelElement = new ModelElement( 'paragraph', null,
+			[ 'x', new ModelText( 'foo', { link: 'http://foo.com' } ), 'x' ] );
+		const viewP = new ViewContainerElement( 'p' );
+
+		const elementGenerator = ( href ) => new ViewAttributeElement( 'a', { href } );
+
+		modelRoot.appendChildren( modelElement );
+		dispatcher.on( 'insert:paragraph', insertElement( viewP ) );
+		dispatcher.on( 'insert:$text', insertText() );
+		dispatcher.on( 'addAttribute:link', wrap( elementGenerator ) );
+		dispatcher.on( 'changeAttribute:link', wrap( elementGenerator ) );
+
+		dispatcher.convertInsert(
+			ModelRange.createFromElement( modelRoot )
+		);
+
+		expect( viewToString( viewRoot ) ).to.equal( '<div><p>x<a href="http://foo.com">foo</a>x</p></div>' );
+
+		for ( let value of ModelRange.createFromElement( modelElement ) ) {
+			value.item.setAttribute( 'link', 'http://foobar.com' );
+		}
+
+		dispatcher.convertAttribute(
+			'changeAttribute',
+			ModelRange.createFromElement( modelElement ),
+			'link',
+			'http://foo.com',
+			'http://foobar.com'
+		);
+
+		expect( viewToString( viewRoot ) ).to.equal( '<div><p><a href="http://foobar.com">xfoox</a></p></div>' );
+	} );
 } );
 
 describe( 'move', () => {

+ 8 - 0
packages/ckeditor5-engine/tests/tickets/475/475.html

@@ -0,0 +1,8 @@
+<head>
+	<meta charset="utf-8">
+	<link rel="stylesheet" href="%APPS_DIR%ckeditor/build/amd/theme/ckeditor.css">
+</head>
+
+<div id="editor">
+	<p>ckeditor from http://www.cksource.com is awesome!</p>
+</div>

+ 92 - 0
packages/ckeditor5-engine/tests/tickets/475/475.js

@@ -0,0 +1,92 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+import ClassicEditor from '/ckeditor5/editor-classic/classic.js';
+
+import Feature from '/ckeditor5/feature.js';
+
+import TreeWalker from '/ckeditor5/engine/model/treewalker.js';
+import Position from '/ckeditor5/engine/model/position.js';
+import Range from '/ckeditor5/engine/model/range.js';
+import LivePosition from '/ckeditor5/engine/model/liveposition.js';
+
+import BuildModelConverterFor from '/ckeditor5/engine/conversion/model-converter-builder.js';
+import BuildViewConverterFor from '/ckeditor5/engine/conversion/view-converter-builder.js';
+
+import AttributeElement from '/ckeditor5/engine/view/attributeelement.js';
+
+export default class Link extends Feature {
+	init() {
+		const editor = this.editor;
+		const data = editor.data;
+		const editing = editor.editing;
+
+		// Allow bold attribute on all inline nodes.
+		editor.document.schema.allow( { name: '$inline', attributes: [ 'link' ] } );
+
+		// Build converter from model to view for data and editing pipelines.
+		BuildModelConverterFor( data.modelToView, editing.modelToView )
+			.fromAttribute( 'link' )
+			.toElement( ( href ) => new AttributeElement( 'a', { href } ) );
+
+		// Build converter from view to model for data pipeline.
+		BuildViewConverterFor( data.viewToModel )
+			.fromElement( 'a' )
+			.toAttribute( ( viewElement ) => ( { key: 'link', value: viewElement.getAttribute( 'href' ) } ) );
+	}
+}
+
+const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/;
+
+export default class AutoLinker extends Feature {
+	init() {
+		this.editor.document.on( 'change', ( event, type, changes, batch ) => {
+			if ( type != 'insert' ) {
+				return;
+			}
+
+			for ( let value of changes.range.getItems( { singleCharacters: true } ) ) {
+				const walker = new TreeWalker( {
+					direction: 'BACKWARD',
+					startPosition: Position.createAfter( value )
+				} );
+
+				const currentValue = walker.next().value;
+				const text = currentValue.item.text;
+
+				if ( !text ) {
+					return;
+				}
+
+				let matchedUrl = urlRegex.exec( text );
+
+				if ( !matchedUrl ) {
+					return;
+				}
+
+				const doc = this.editor.document;
+				const url = matchedUrl[ 0 ];
+				const offset = _getLastPathPart( currentValue.nextPosition.path ) + matchedUrl.index;
+				const livePos = LivePosition.createFromParentAndOffset( currentValue.item.commonParent, offset );
+
+				doc.enqueueChanges( () => {
+					const urlRange = Range.createFromPositionAndShift( livePos, url.length );
+					batch.setAttr( 'link', url, urlRange );
+				} );
+			}
+		} );
+	}
+}
+
+function _getLastPathPart( path ) {
+	return path[ path.length - 1 ];
+}
+
+ClassicEditor.create( document.querySelector( '#editor' ), {
+	features: [ 'delete', 'enter', 'typing', 'paragraph', 'undo', Link, AutoLinker ],
+	toolbar: [ 'undo', 'redo' ]
+} );

+ 11 - 0
packages/ckeditor5-engine/tests/tickets/475/475.md

@@ -0,0 +1,11 @@
+@bender-ui: collapsed
+@bender-tags: ticket, 475
+
+### Auto-linking manual test [#475](https://github.com/ckeditor/ckeditor5-engine/issues/475)
+
+ - put the caret at the end of the line,
+ - type the link (http://cksource.com).
+
+Expected result: link should appear where you typed it. It should change the `src` property as you type.
+
+Note: this is not a full feature, some other cases may not work.