Przeglądaj źródła

Changed the way of setting selection characters in stringify + add the same option to parse.

Oskar Wrobel 9 lat temu
rodzic
commit
eee2ffa180

+ 3 - 3
packages/ckeditor5-engine/tests/_utils-tests/view.js

@@ -186,7 +186,7 @@ describe( 'view test utils', () => {
 			expect( stringify( p, selection ) ).to.equal( '<p><b>f{ooba}r</b><b>bazqux</b></p>' );
 		} );
 
-		it( 'should write selection ranges inside text represented by custom characters', () => {
+		it( 'should write selection ranges inside text represented by `[` and `]` characters', () => {
 			const text1 = new Text( 'foobar' );
 			const text2 = new Text( 'bazqux' );
 			const b1 = new Element( 'b', null, text1 );
@@ -195,8 +195,8 @@ describe( 'view test utils', () => {
 			const range = Range.createFromParentsAndOffsets( text1, 1, text1, 5 );
 			const selection = new Selection();
 			selection.addRange( range );
-			expect( stringify( p, selection, { characterForSelectionInText: [ '_SELECTION-START_', '_SELECTION-END_' ] } ) )
-				.to.equal( '<p><b>f_SELECTION-START_ooba_SELECTION-END_r</b><b>bazqux</b></p>' );
+			expect( stringify( p, selection, { sameSelectionCharacters: true } ) )
+				.to.equal( '<p><b>f[ooba]r</b><b>bazqux</b></p>' );
 		} );
 
 		it( 'should write collapsed selection ranges inside texts', () => {

+ 2 - 2
packages/ckeditor5-engine/tests/_utils/model.js

@@ -196,7 +196,7 @@ export function stringify( node, selectionOrPositionOrRange = null ) {
 	mapper.clearBindings();
 
 	// Parse view to data string.
-	let data = viewStringify( viewDocumentFragment, viewSelection, { characterForSelectionInText: [ '[', ']' ] } );
+	let data = viewStringify( viewDocumentFragment, viewSelection, { sameSelectionCharacters: true } );
 
 	// Replace valid XML text element name to `$text`.
 	return data.replace( new RegExp( VIEW_TEXT_WITH_ATTRIBUTES_ELEMENT, 'g' ), DATA_STRING_TEXT_WITH_ATTRIBUTES_ELEMENT );
@@ -221,7 +221,7 @@ export function parse( data, schema, mapper = new Mapper() ) {
 	data = data.replace( new RegExp( '\\' + DATA_STRING_TEXT_WITH_ATTRIBUTES_ELEMENT, 'g' ), VIEW_TEXT_WITH_ATTRIBUTES_ELEMENT );
 
 	// Parse data to view using view utils.
-	const parsedResult = viewParse( data );
+	const parsedResult = viewParse( data, { sameSelectionCharacters: true } );
 
 	// Retrieve DocumentFragment and Selection from parsed view.
 	let viewDocumentFragment, selection;

+ 50 - 21
packages/ckeditor5-engine/tests/_utils/view.js

@@ -126,10 +126,9 @@ setData._parse = parse;
  *
  *		stringify( p, selection ); // '<p><b>f{ooba}r</b></p>'
  *
- * NOTE:
- * Characters representing range placed inside text are customizable by `characterForSelectionInText` option.
- * It is mainly needed when view stringify function is used by model stringify util, then range
- * is represented with `[` and `]`.
+ * ** Note: **
+ * It is possible to unify selection markers to `[` and `]` for both (inside and outside text)
+ * by setting `sameSelectionCharacters=true` option. It is mainly used when view stringify option is used by model utils.
  *
  * Multiple ranges are supported:
  *
@@ -181,9 +180,8 @@ setData._parse = parse;
  * (`<span view-priority="12">`, `<b view-priority="10">`).
  * @param {Boolean} [options.ignoreRoot=false] When set to `true` root's element opening and closing will not be printed.
  * Mainly used by `getData` function to ignore {@link engine.view.Document Document's} root element.
- * @param {Array<String>} [options.characterForSelectionInText=['{','}']] At default range placed inside text is
- * represented with `{` and `}` characters, but when stringify function is used by model stringify util then characters
- * are changed to `[` and `]`.
+ * @param {Boolean} [options.sameSelectionCharacters=false] When set to `true` then selection inside text will be marked as `{` and `}`
+ * and selection outside text as `[` and `]`. When set to `false` then both will be marked as `[` and `]` only.
  * @returns {String} HTML-like string representing the view.
  */
 export function stringify( node, selectionOrPositionOrRange = null, options = {} ) {
@@ -199,10 +197,6 @@ export function stringify( node, selectionOrPositionOrRange = null, options = {}
 		selection = selectionOrPositionOrRange;
 	}
 
-	if ( !options.characterForSelectionInText ) {
-		options.characterForSelectionInText = [ TEXT_RANGE_START_TOKEN, TEXT_RANGE_END_TOKEN ];
-	}
-
 	const viewStringify = new ViewStringify( node, selection, options );
 
 	return viewStringify.stringify();
@@ -232,6 +226,10 @@ export function stringify( node, selectionOrPositionOrRange = null, options = {}
  *
  *		const { root, selection } = parse( '<p>[<b>foobar</b>]</p>' );
  *
+ * ** Note: **
+ * It is possible to unify selection markers to `[` and `]` for both (inside and outside text)
+ * by setting `sameSelectionCharacters=true` option. It is mainly used when view parse option is used by model utils.
+ *
  * Sometimes there is a need for defining order of ranges inside created selection. This can be achieved by providing
  * ranges order array as additional parameter:
  *
@@ -268,12 +266,16 @@ export function stringify( node, selectionOrPositionOrRange = null, options = {}
  * When set to `null` root element will be created automatically. If set to
  * {@link engine.view.Element Element} or {@link engine.view.DocumentFragment DocumentFragment} - this node
  * will be used as root for all parsed nodes.
+ * @param {Boolean} [options.sameSelectionCharacters=false] When set to `true` then selection inside text should be marked using `{` and `}`
+ * and selection outside text using `[` and `]`. When set to `false` then both should be marked with `[` and `]` only.
  * @returns {engine.view.Text|engine.view.Element|engine.view.DocumentFragment|Object} Returns parsed view node
  * or object with two fields `view` and `selection` when selection ranges were included in data to parse.
  */
 export function parse( data, options = {} ) {
 	options.order = options.order || [];
-	const rangeParser = new RangeParser();
+	const rangeParser = new RangeParser( {
+		sameSelectionCharacters: options.sameSelectionCharacters
+	} );
 	const processor = new XmlDataProcessor();
 
 	// Convert data to view.
@@ -327,6 +329,18 @@ export function parse( data, options = {} ) {
  * @private
  */
 class RangeParser {
+
+	/**
+	 * Create RangeParser instance.
+	 *
+	 * @param {Object} options RangeParser configuration.
+	 * @param {Boolean} [options.sameSelectionCharacters=false] When set to `true` it means that selection inside text is marked as
+	 * `{` and `}` and selection outside text as `[` and `]`. When set to `false` then both are marked as `[` and `]`.
+	 */
+	constructor( options ) {
+		this.sameSelectionCharacters = !!options.sameSelectionCharacters;
+	}
+
 	/**
 	 * Parses the view, and returns ranges represented inside {@link engine.view.Text Text nodes}.
 	 * Method will remove all occurrences of `{`, `}`, `[` and `]` from found text nodes. If text node is empty after
@@ -396,11 +410,13 @@ class RangeParser {
 
 				brackets.push( {
 					bracket: bracket,
-					textOffset: index - offset
+					textOffset: index - offset,
+					outer: index === 0 || index == node._data.length - 1
 				} );
 
 				offset++;
 			}
+
 			text = text.replace( regexp, '' );
 			node.data = text;
 			const index = node.index;
@@ -414,7 +430,10 @@ class RangeParser {
 			for ( let item of brackets ) {
 				// Non-empty text node.
 				if ( text ) {
-					if ( item.bracket == TEXT_RANGE_START_TOKEN || item.bracket == TEXT_RANGE_END_TOKEN ) {
+					if (
+						( this.sameSelectionCharacters && !item.outer ) ||
+						( !this.sameSelectionCharacters && ( item.bracket == TEXT_RANGE_START_TOKEN || item.bracket == TEXT_RANGE_END_TOKEN ) )
+					) {
 						// Store information about text range delimiter.
 						this._positions.push( {
 							bracket: item.bracket,
@@ -422,7 +441,7 @@ class RangeParser {
 						} );
 					} else {
 						// Check if element range delimiter is not placed inside text node.
-						if ( item.textOffset !== 0 && item.textOffset !== text.length ) {
+						if ( !this.sameSelectionCharacters && item.textOffset !== 0 && item.textOffset !== text.length ) {
 							throw new Error( `Parse error - range delimiter '${ item.bracket }' is placed inside text node.` );
 						}
 
@@ -436,7 +455,7 @@ class RangeParser {
 						} );
 					}
 				} else {
-					if ( item.bracket == TEXT_RANGE_START_TOKEN || item.bracket == TEXT_RANGE_END_TOKEN ) {
+					if ( !this.sameSelectionCharacters && item.bracket == TEXT_RANGE_START_TOKEN || item.bracket == TEXT_RANGE_END_TOKEN ) {
 						throw new Error( `Parse error - text range delimiter '${ item.bracket }' is placed inside empty text node. ` );
 					}
 
@@ -533,7 +552,8 @@ class ViewStringify {
 	 * @param {Boolean} [options.showPriority=false] When set to `true` AttributeElement's priority will be printed.
 	 * @param {Boolean} [options.ignoreRoot=false] When set to `true` root's element opening and closing tag will not
 	 * be outputted.
-	 * @param {Array<String>} [options.characterForSelectionInText = [ '{', '}' ]
+	 * @param {Boolean} [options.sameSelectionCharacters=false] When set to `true` it means that selection inside text is marked as
+	 * `{` and `}` and selection outside text as `[` and `]`. When set to `false` then both are marked as `[` and `]`.
 	 */
 	constructor( root, selection = null, options = {} ) {
 		this.root = root;
@@ -547,7 +567,7 @@ class ViewStringify {
 		this.showType = !!options.showType;
 		this.showPriority = !!options.showPriority;
 		this.ignoreRoot = !!options.ignoreRoot;
-		this.characterForSelectionInText = options.characterForSelectionInText;
+		this.sameSelectionCharacters = !!options.sameSelectionCharacters;
 	}
 
 	/**
@@ -641,6 +661,15 @@ class ViewStringify {
 	_stringifyTextRanges( node ) {
 		const length = node.data.length;
 		let result = node.data.split( '' );
+		let rangeStartToken, rangeEndToken;
+
+		if ( this.sameSelectionCharacters ) {
+			rangeStartToken = ELEMENT_RANGE_START_TOKEN;
+			rangeEndToken = ELEMENT_RANGE_END_TOKEN;
+		} else {
+			rangeStartToken = TEXT_RANGE_START_TOKEN;
+			rangeEndToken = TEXT_RANGE_END_TOKEN;
+		}
 
 		// Add one more element for ranges ending after last character in text.
 		result[ length ] = '';
@@ -661,14 +690,14 @@ class ViewStringify {
 
 			if ( start.parent == node && start.offset >= 0 && start.offset <= length ) {
 				if ( range.isCollapsed ) {
-					result[ end.offset ].collapsed += this.characterForSelectionInText.join( '' );
+					result[ end.offset ].collapsed += rangeStartToken + rangeEndToken;
 				} else {
-					result[ start.offset ].start += this.characterForSelectionInText[ 0 ];
+					result[ start.offset ].start += rangeStartToken;
 				}
 			}
 
 			if ( end.parent == node && end.offset >= 0 && end.offset <= length && !range.isCollapsed  ) {
-				result[ end.offset ].end += this.characterForSelectionInText[ 1 ];
+				result[ end.offset ].end += rangeEndToken;
 			}
 		}