Przeglądaj źródła

Merge pull request #687 from ckeditor/t/686

Add findAncestor method to view.Element.
Piotrek Koszuliński 9 lat temu
rodzic
commit
39a1af2a40

+ 24 - 0
packages/ckeditor5-engine/src/view/element.js

@@ -8,6 +8,7 @@ import Text from './text.js';
 import objectToMap from '../../utils/objecttomap.js';
 import isIterable from '../../utils/isiterable.js';
 import isPlainObject from '../../utils/lib/lodash/isPlainObject.js';
+import Matcher from './matcher.js';
 
 /**
  * View element.
@@ -568,6 +569,29 @@ export default class Element extends Node {
 		this._fireChange( 'attributes', this );
 		property.forEach( name => this._styles.delete( name ) );
 	}
+
+	/**
+	 * Returns ancestor element that match specified pattern.
+	 * Provided patterns should be compatible with {@link engine.view.Matcher Matcher} as it is used internally.
+	 *
+	 * @see engine.view.Matcher
+	 * @param {Object|String|RegExp|Function} patterns Patterns used to match correct ancestor. See {@link engine.view.Matcher}.
+	 * @returns {engine.view.Element|null} Found element or `null` if no matching ancestor was found.
+	 */
+	findAncestor( ...patterns ) {
+		const matcher = new Matcher( ...patterns );
+		let parent = this.parent;
+
+		while ( parent ) {
+			if ( matcher.match( parent ) ) {
+				return parent;
+			}
+
+			parent = parent.parent;
+		}
+
+		return null;
+	}
 }
 
 // Parses inline styles and puts property - value pairs into styles map.

+ 3 - 3
packages/ckeditor5-engine/src/view/matcher.js

@@ -86,7 +86,7 @@ export default class Matcher {
 	 *
 	 * 		matcher.add( 'div', { class: 'foobar' } );
 	 *
-	 * @param {Object|String|RegExp|function} pattern Object describing pattern details. If string or regular expression
+	 * @param {Object|String|RegExp|Function} pattern Object describing pattern details. If string or regular expression
 	 * is provided it will be used to match element's name. Pattern can be also provided in a form
 	 * of a function - then this function will be called with each {@link engine.view.Element element} as a parameter.
 	 * Function's return value will be stored under `match` key of the object returned from
@@ -139,7 +139,7 @@ export default class Matcher {
 	 * @param {...core.view.Element} element View element to match against stored patterns.
 	 * @returns {Object|null} result
 	 * @returns {core.view.Element} result.element Matched view element.
-	 * @returns {Object|String|RegExp|function} result.pattern Pattern that was used to find matched element.
+	 * @returns {Object|String|RegExp|Function} result.pattern Pattern that was used to find matched element.
 	 * @returns {Object} result.match Object representing matched element parts.
 	 * @returns {Boolean} [result.match.name] True if name of the element was matched.
 	 * @returns {Array} [result.match.attribute] Array with matched attribute names.
@@ -212,7 +212,7 @@ export default class Matcher {
 // If element cannot be matched to provided pattern - returns `null`.
 //
 // @param {engine.view.Element} element
-// @param {Object|String|RegExp|function} pattern
+// @param {Object|String|RegExp|Function} pattern
 // @returns {Object|null} Returns object with match information or null if element is not matching.
 function isElementMatching( element, pattern ) {
 	// If pattern is provided as function - return result of that function;

+ 33 - 0
packages/ckeditor5-engine/tests/view/element.js

@@ -784,4 +784,37 @@ describe( 'Element', () => {
 			} );
 		} );
 	} );
+
+	describe( 'findAncestor', () => {
+		it( 'should return null if element have no ancestor', () => {
+			const el = new Element( 'p' );
+
+			expect( el.findAncestor( 'div' ) ).to.be.null;
+		} );
+
+		it( 'should return ancestor if matching', () => {
+			const el1 = new Element( 'p' );
+			const el2 = new Element( 'div', null, el1 );
+
+			expect( el1.findAncestor( 'div' ) ).to.equal( el2 );
+		} );
+
+		it( 'should return parent\'s ancestor if matching', () => {
+			const el1 = new Element( 'p' );
+			const el2 = new Element( 'div', null, el1 );
+			const el3 = new Element( 'div', { class: 'foo bar' }, el2 );
+
+			expect( el1.findAncestor( { class: 'foo' } ) ).to.equal( el3 );
+		} );
+
+		it( 'should return null if no matches found', () => {
+			const el1 = new Element( 'p' );
+			new Element( 'div', null, el1 );
+
+			expect( el1.findAncestor( {
+				name: 'div',
+				class: 'container'
+			} ) ).to.be.null;
+		} );
+	} );
 } );