Explorar o código

Merge branch 'master' into i/7983

Aleksander Nowodzinski %!s(int64=5) %!d(string=hai) anos
pai
achega
83f88a1b04

+ 20 - 11
packages/ckeditor5-link/src/linkui.js

@@ -589,18 +589,27 @@ export default class LinkUI extends Plugin {
 		const view = this.editor.editing.view;
 		const model = this.editor.model;
 		const viewDocument = view.document;
-		const targetLink = this._getSelectedLinkElement();
-		const range = model.markers.has( VISUAL_SELECTION_MARKER_NAME ) ?
+		let target = null;
+
+		if ( model.markers.has( VISUAL_SELECTION_MARKER_NAME ) ) {
 			// There are cases when we highlight selection using a marker (#7705, #4721).
-			this.editor.editing.mapper.toViewRange( model.markers.get( VISUAL_SELECTION_MARKER_NAME ).getRange() ) :
-			// If no markers are available refer to a regular selection.
-			viewDocument.selection.getFirstRange();
-
-		const target = targetLink ?
-			// When selection is inside link element, then attach panel to this element.
-			view.domConverter.mapViewToDom( targetLink ) :
-			// Otherwise attach panel to the selection.
-			view.domConverter.viewRangeToDom( range );
+			const markerViewElements = Array.from( this.editor.editing.mapper.markerNameToElements( VISUAL_SELECTION_MARKER_NAME ) );
+			const newRange = view.createRange(
+				view.createPositionBefore( markerViewElements[ 0 ] ),
+				view.createPositionAfter( markerViewElements[ markerViewElements.length - 1 ] )
+			);
+
+			target = view.domConverter.viewRangeToDom( newRange );
+		} else {
+			const targetLink = this._getSelectedLinkElement();
+			const range = viewDocument.selection.getFirstRange();
+
+			target = targetLink ?
+				// When selection is inside link element, then attach panel to this element.
+				view.domConverter.mapViewToDom( targetLink ) :
+				// Otherwise attach panel to the selection.
+				view.domConverter.viewRangeToDom( range );
+		}
 
 		return { target };
 	}

+ 36 - 24
packages/ckeditor5-link/tests/linkui.js

@@ -7,6 +7,8 @@
 
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import indexOf from '@ckeditor/ckeditor5-utils/src/dom/indexof';
+import isRange from '@ckeditor/ckeditor5-utils/src/dom/isrange';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
@@ -134,49 +136,37 @@ describe( 'LinkUI', () => {
 
 		it( 'should add #formView to the balloon and attach the balloon to the selection when text fragment is selected', () => {
 			setModelData( editor.model, '<paragraph>f[o]o</paragraph>' );
-			const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
 
 			linkUIFeature._showUI();
 
+			const expectedRange = getMarkersRange( editor );
+
 			expect( balloon.visibleView ).to.equal( formView );
 			sinon.assert.calledWithExactly( balloonAddSpy, {
 				view: formView,
 				position: {
-					target: selectedRange
+					target: sinon.match( isRange )
 				}
 			} );
+
+			assertDomRange( expectedRange, balloonAddSpy.args[ 0 ][ 0 ].position.target );
 		} );
 
-		it( 'should add #formView to the balloon and attach the balloon to the selection when selection is collapsed', () => {
+		it( 'should add #formView to the balloon and attach the balloon to the marker element when selection is collapsed', () => {
+			// (#7926)
 			setModelData( editor.model, '<paragraph>f[]oo</paragraph>' );
-			const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
-
 			linkUIFeature._showUI();
 
+			const expectedRange = getMarkersRange( editor );
+
 			expect( balloon.visibleView ).to.equal( formView );
 			sinon.assert.calledWithExactly( balloonAddSpy, {
 				view: formView,
 				position: {
-					target: selectedRange
+					target: sinon.match( isRange )
 				}
 			} );
-		} );
-
-		it( 'should pass a proper position target to the balloon toolbar', () => {
-			setModelData( editor.model, '<paragraph>f[o]o</paragraph>' );
-
-			linkUIFeature._showUI();
-
-			const markerModelRange = editor.model.markers.get( 'link-ui' ).getRange();
-			const markerViewRange = editor.editing.mapper.toViewRange( markerModelRange );
-			const domRange = editor.editing.view.domConverter.viewRangeToDom( markerViewRange );
-
-			expect( balloonAddSpy.calledWithExactly( {
-				view: formView,
-				position: {
-					target: domRange
-				}
-			} ), 'spy arguments' ).to.be.true;
+			assertDomRange( expectedRange, balloonAddSpy.args[ 0 ][ 0 ].position.target );
 		} );
 
 		it( 'should add #actionsView to the balloon and attach the balloon to the link element when collapsed selection is inside ' +
@@ -380,10 +370,14 @@ describe( 'LinkUI', () => {
 					writer.setSelection( text, 1, true );
 				} );
 
+				const expectedRange = getMarkersRange( editor );
+
 				sinon.assert.calledOnce( spy );
 				sinon.assert.calledWithExactly( spy, {
-					target: editorElement.ownerDocument.getSelection().getRangeAt( 0 )
+					target: sinon.match( isRange )
 				} );
+
+				assertDomRange( expectedRange, spy.args[ 0 ][ 0 ].target );
 			} );
 
 			it( 'not update the position when is in not visible stack', () => {
@@ -524,6 +518,24 @@ describe( 'LinkUI', () => {
 			);
 			expect( editor.getData() ).to.equal( '<p>fo</p>' );
 		} );
+
+		function getMarkersRange( editor ) {
+			const markerElements = editor.ui.view.element.querySelectorAll( '.ck-fake-link-selection' );
+			const lastMarkerElement = markerElements[ markerElements.length - 1 ];
+
+			const range = document.createRange();
+			range.setStart( markerElements[ 0 ].parentElement, indexOf( markerElements[ 0 ] ) );
+			range.setEnd( lastMarkerElement.parentElement, indexOf( lastMarkerElement ) + 1 );
+
+			return range;
+		}
+
+		function assertDomRange( expected, actual ) {
+			expect( actual, 'startContainer' ).to.have.property( 'startContainer', expected.startContainer );
+			expect( actual, 'startOffset' ).to.have.property( 'startOffset', expected.startOffset );
+			expect( actual, 'endContainer' ).to.have.property( 'endContainer', expected.endContainer );
+			expect( actual, 'endOffset' ).to.have.property( 'endOffset', expected.endOffset );
+		}
 	} );
 
 	describe( '_hideUI()', () => {

+ 48 - 41
packages/ckeditor5-list/src/liststyleediting.js

@@ -189,7 +189,6 @@ export default class ListStyleEditing extends Plugin {
 // Returns a converter that consumes the `style` attribute and search for `list-style-type` definition.
 // If not found, the `"default"` value will be used.
 //
-// @private
 // @returns {Function}
 function upcastListItemStyle() {
 	return dispatcher => {
@@ -206,7 +205,6 @@ function upcastListItemStyle() {
 // Returns a converter that adds the `list-style-type` definition as a value for the `style` attribute.
 // The `"default"` value is removed and not present in the view/data.
 //
-// @private
 // @returns {Function}
 function downcastListStyleAttribute() {
 	return dispatcher => {
@@ -271,7 +269,6 @@ function downcastListStyleAttribute() {
 //     ○ List item 2.[]
 // ■ List item 3.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListAfterIndentListCommand( editor ) {
@@ -323,7 +320,6 @@ function fixListAfterIndentListCommand( editor ) {
 // ■ List item 2.[]
 // ■ List item 3.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListAfterOutdentListCommand( editor ) {
@@ -423,22 +419,17 @@ function fixListAfterOutdentListCommand( editor ) {
 // ■ List item 2.  // ...
 // ■ List item 3.  // ...
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function fixListStyleAttributeOnListItemElements( editor ) {
 	return writer => {
 		let wasFixed = false;
-		let insertedListItems = [];
 
-		for ( const change of editor.model.document.differ.getChanges() ) {
-			if ( change.type == 'insert' && change.name == 'listItem' ) {
-				insertedListItems.push( change.position.nodeAfter );
-			}
-		}
-
-		// Don't touch todo lists.
-		insertedListItems = insertedListItems.filter( item => item.getAttribute( 'listType' ) !== 'todo' );
+		const insertedListItems = getChangedListItems( editor.model.document.differ.getChanges() )
+			.filter( item => {
+				// Don't touch todo lists. They are handled in another post-fixer.
+				return item.getAttribute( 'listType' ) !== 'todo';
+			} );
 
 		if ( !insertedListItems.length ) {
 			return wasFixed;
@@ -474,7 +465,7 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 
 		for ( const item of insertedListItems ) {
 			if ( !item.hasAttribute( 'listStyle' ) ) {
-				if ( shouldInheritListType( existingListItem ) ) {
+				if ( shouldInheritListType( existingListItem, item ) ) {
 					writer.setAttribute( 'listStyle', existingListItem.getAttribute( 'listStyle' ), item );
 				} else {
 					writer.setAttribute( 'listStyle', DEFAULT_LIST_TYPE, item );
@@ -493,8 +484,9 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 	// the value for the element is other than default in the base element.
 	//
 	// @param {module:engine/model/element~Element|null} baseItem
+	// @param {module:engine/model/element~Element} itemToChange
 	// @returns {Boolean}
-	function shouldInheritListType( baseItem ) {
+	function shouldInheritListType( baseItem, itemToChange ) {
 		if ( !baseItem ) {
 			return false;
 		}
@@ -509,28 +501,25 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 			return false;
 		}
 
+		if ( baseItem.getAttribute( 'listType' ) !== itemToChange.getAttribute( 'listType' ) ) {
+			return false;
+		}
+
 		return true;
 	}
 }
 
 // Removes the `listStyle` attribute from "todo" list items.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function removeListStyleAttributeFromTodoList( editor ) {
 	return writer => {
-		let todoListItems = [];
-
-		for ( const change of editor.model.document.differ.getChanges() ) {
-			const item = getItemFromChange( change );
-
-			if ( item && item.is( 'element', 'listItem' ) && item.getAttribute( 'listType' ) === 'todo' ) {
-				todoListItems.push( item );
-			}
-		}
-
-		todoListItems = todoListItems.filter( item => item.hasAttribute( 'listStyle' ) );
+		const todoListItems = getChangedListItems( editor.model.document.differ.getChanges() )
+			.filter( item => {
+				// Handle the todo lists only. The rest is handled in another post-fixer.
+				return item.getAttribute( 'listType' ) === 'todo' && item.hasAttribute( 'listStyle' );
+			} );
 
 		if ( !todoListItems.length ) {
 			return false;
@@ -542,23 +531,10 @@ function removeListStyleAttributeFromTodoList( editor ) {
 
 		return true;
 	};
-
-	function getItemFromChange( change ) {
-		if ( change.type === 'attribute' ) {
-			return change.range.start.nodeAfter;
-		}
-
-		if ( change.type === 'insert' ) {
-			return change.position.nodeAfter;
-		}
-
-		return null;
-	}
 }
 
 // Restores the `listStyle` attribute after changing the list type.
 //
-// @private
 // @param {module:core/editor/editor~Editor} editor
 // @returns {Function}
 function restoreDefaultListStyle( editor ) {
@@ -573,3 +549,34 @@ function restoreDefaultListStyle( editor ) {
 		} );
 	};
 }
+
+// Returns `listItem` that were inserted or changed.
+//
+// @param {Array.<Object>} changes The changes list returned by the differ.
+// @returns {Array.<module:engine/model/element~Element>}
+function getChangedListItems( changes ) {
+	const items = [];
+
+	for ( const change of changes ) {
+		const item = getItemFromChange( change );
+
+		if ( item && item.is( 'element', 'listItem' ) ) {
+			items.push( item );
+		}
+	}
+
+	return items;
+}
+
+function getItemFromChange( change ) {
+	if ( change.type === 'attribute' ) {
+		return change.range.start.nodeAfter;
+	}
+
+	if ( change.type === 'insert' ) {
+		return change.position.nodeAfter;
+	}
+
+	return null;
+}
+

+ 92 - 4
packages/ckeditor5-list/tests/liststyleediting.js

@@ -452,19 +452,38 @@ describe( 'ListStyleEditing', () => {
 			it( 'should not inherit the list style attribute when merging different kind of lists (from top, merge a single item)', () => {
 				setModelData( model,
 					'<paragraph>Foo Bar.[]</paragraph>' +
-					'<listItem listIndent="0" listStyle="default"  listType="numbered">Foo</listItem>' +
-					'<listItem listIndent="0" listStyle="default"  listType="numbered">Bar</listItem>'
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
 				);
 
 				editor.execute( 'bulletedList' );
 
 				expect( getModelData( model ) ).to.equal(
 					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
-					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo</listItem>' +
-					'<listItem listIndent="0" listStyle="default" listType="numbered">Bar</listItem>'
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>'
 				);
 			} );
 
+			it(
+				'should not inherit the list style attribute when merging different kind of lists (from bottom, merge a single item)',
+				() => {
+					setModelData( model,
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
+						'<paragraph>Foo Bar.[]</paragraph>'
+					);
+
+					editor.execute( 'bulletedList' );
+
+					expect( getModelData( model ) ).to.equal(
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="decimal-leading-zero" listType="numbered">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+				}
+			);
+
 			it( 'should inherit the list style attribute when merging the same kind of lists (from bottom, merge a single item)', () => {
 				setModelData( model,
 					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
@@ -503,6 +522,75 @@ describe( 'ListStyleEditing', () => {
 			);
 		} );
 
+		describe( 'modifying "listType" attribute', () => {
+			it( 'should inherit the list style attribute when the modified list is the same kind of the list as next sibling', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				editor.execute( 'bulletedList' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it( 'should inherit the list style attribute when the modified list is the same kind of the list as previous sibling', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+					'<listItem listIndent="0" listStyle="default" listType="numbered">Foo Bar.[]</listItem>'
+				);
+
+				editor.execute( 'bulletedList' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo Bar.[]</listItem>'
+				);
+			} );
+
+			it( 'should not inherit the list style attribute when the modified list already has defined it (next sibling check)', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+
+				editor.execute( 'listStyle', { type: 'disc' } );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>'
+				);
+			} );
+
+			it(
+				'should not inherit the list style attribute when the modified list already has defined it (previous sibling check)',
+				() => {
+					setModelData( model,
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+
+					editor.execute( 'listStyle', { type: 'disc' } );
+
+					expect( getModelData( model ) ).to.equal(
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Foo</listItem>' +
+						'<listItem listIndent="0" listStyle="circle" listType="bulleted">Bar</listItem>' +
+						'<listItem listIndent="0" listStyle="disc" listType="bulleted">Foo Bar.[]</listItem>'
+					);
+				}
+			);
+		} );
+
 		describe( 'indenting lists', () => {
 			it( 'should restore the default value for the list style attribute when indenting a single item', () => {
 				setModelData( model,

+ 16 - 16
scripts/switch-to-dev-dev.sh

@@ -13,42 +13,42 @@ if [ ! -d node_modules/@ckeditor ]; then
   mkdir node_modules/@ckeditor
 fi
 
-echo "Linking ckeditor5-dev-docs..."
+echo "Linking packages from ckeditor5-dev..."
 
+echo "Linking ckeditor5-dev-docs..."
 rm -rf node_modules/@ckeditor/ckeditor5-dev-docs
 ln -s ../../../ckeditor5-dev/packages/ckeditor5-dev-docs node_modules/@ckeditor
 
 echo "Linking ckeditor5-dev-env..."
-
 rm -rf node_modules/@ckeditor/ckeditor5-dev-env
 ln -s ../../../ckeditor5-dev/packages/ckeditor5-dev-env node_modules/@ckeditor
 
 echo "Linking ckeditor5-dev-tests..."
-
 rm -rf node_modules/@ckeditor/ckeditor5-dev-tests
 ln -s ../../../ckeditor5-dev/packages/ckeditor5-dev-tests node_modules/@ckeditor
 
 echo "Linking ckeditor5-dev-utils..."
-
 rm -rf node_modules/@ckeditor/ckeditor5-dev-utils
 ln -s ../../../ckeditor5-dev/packages/ckeditor5-dev-utils node_modules/@ckeditor
 
-echo "Linking eslint-config-ckeditor5..."
-
-rm -rf node_modules/eslint-config-ckeditor5
-ln -s ../../ckeditor5-dev/packages/eslint-config-ckeditor5 node_modules/
-
-echo "Linking stylelint-config-ckeditor5..."
-
-rm -rf node_modules/stylelint-config-ckeditor5
-ln -s ../../ckeditor5-dev/packages/stylelint-config-ckeditor5 node_modules/
-
 echo "Linking jsdoc-plugins..."
-
 rm -rf node_modules/@ckeditor/jsdoc-plugins
 ln -s ../../../ckeditor5-dev/packages/jsdoc-plugins node_modules/@ckeditor
 
 echo "Linking ckeditor5-dev-webpack-plugin..."
-
 rm -rf node_modules/@ckeditor/ckeditor5-dev-webpack-plugin
 ln -s ../../../ckeditor5-dev/packages/ckeditor5-dev-webpack-plugin node_modules/@ckeditor
+
+echo "Linking linters packages..."
+
+echo "Linking eslint-config-ckeditor5..."
+rm -rf node_modules/eslint-config-ckeditor5
+ln -s ../../eslint-config-ckeditor5 node_modules/
+
+echo "Linking eslint-plugin-ckeditor5-rules..."
+rm -rf node_modules/eslint-plugin-ckeditor5-rules
+ln -s ../../eslint-plugin-ckeditor5-rules node_modules/
+
+echo "Linking stylelint-config-ckeditor5..."
+rm -rf node_modules/stylelint-config-ckeditor5
+ln -s ../../stylelint-config-ckeditor5 node_modules/