Skip to content

Commit

Permalink
Merge branch 't/12279'
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Sep 4, 2014
2 parents b60bb80 + 09ff570 commit 8749148
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Expand Up @@ -3,6 +3,10 @@ CKEditor 4 Changelog

## CKEditor 4.4.5

New Features:

* [#12279](http://dev.ckeditor.com/ticket/12279): Added possibility to pass custom evaluator to [`node.getAscendant()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.node-method-getAscendant).

Fixed Issues:

* [#12381](http://dev.ckeditor.com/ticket/12381): Fixed: Selection trouble in iOS. Thanks to [Remiremi](https://github.com/Remiremi)!
Expand Down
43 changes: 34 additions & 9 deletions core/dom/node.js
Expand Up @@ -539,40 +539,65 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, {
},

/**
* Gets the closest ancestor node of this node, specified by its name.
* Gets the closest ancestor node of this node, specified by its name or using an evaluator function.
*
* // Suppose we have the following HTML structure:
* // <div id="outer"><div id="inner"><p><b>Some text</b></p></div></div>
* // If node == <b>
* ascendant = node.getAscendant( 'div' ); // ascendant == <div id="inner">
* ascendant = node.getAscendant( 'b' ); // ascendant == null
* ascendant = node.getAscendant( 'b', true ); // ascendant == <b>
* ascendant = node.getAscendant( { div:1,p:1 } ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
* ascendant = node.getAscendant( { div:1, p:1 } ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
*
* // Using custom evaluator:
* ascendant = node.getAscendant( function( el ) {
* return el.getId() == 'inner';
* } );
* // ascendant == <div id="inner">
*
* @since 3.6.1
* @param {String} reference The name of the ancestor node to search or
* an object with the node names to search for.
* @param {String/Function/Object} query The name of the ancestor node to search or
* an object with the node names to search for or evaluator function.
* @param {Boolean} [includeSelf] Whether to include the current
* node in the search.
* @returns {CKEDITOR.dom.node} The located ancestor node or null if not found.
* @returns {CKEDITOR.dom.node} The located ancestor node or `null` if not found.
*/
getAscendant: function( reference, includeSelf ) {
getAscendant: function( query, includeSelf ) {
var $ = this.$,
name;
evaluator,
isCustomEvaluator;

if ( !includeSelf )
if ( !includeSelf ) {
$ = $.parentNode;
}

// Custom checker provided in an argument.
if ( typeof query == 'function' ) {
isCustomEvaluator = true;
evaluator = query;
} else {
// Predefined tag name checker.
isCustomEvaluator = false;
evaluator = function( $ ) {
var name = ( typeof $.nodeName == 'string' ? $.nodeName.toLowerCase() : '' );

return ( typeof query == 'string' ? name == query : name in query );
};
}

while ( $ ) {
if ( $.nodeName && ( name = $.nodeName.toLowerCase(), ( typeof reference == 'string' ? name == reference : name in reference ) ) )
// For user provided checker we use CKEDITOR.dom.node.
if ( evaluator( isCustomEvaluator ? new CKEDITOR.dom.node( $ ) : $ ) ) {
return new CKEDITOR.dom.node( $ );
}

try {
$ = $.parentNode;
} catch ( e ) {
$ = null;
}
}

return null;
},

Expand Down
1 change: 1 addition & 0 deletions tests/core/dom/node.html
Expand Up @@ -26,6 +26,7 @@ <h1>Title</h1>
<div id="move"><p id="move1"></p><p id="move2"></p><p id="move3"></p><p id="move4"></p></div>
<div id="remove"><p id="remove1"><i></i>text<!--comment--></p></div>
<div id="replace">1<p id="replace1"></p>2<p id="replace2"></p>3</div>
<div id="deep2" class="deep2"><div class="deep1"><p class="deep0" id="getAscendantFuncCheck"></p></div></div>
<p id="trim">

text
Expand Down
52 changes: 50 additions & 2 deletions tests/core/dom/node.js
@@ -1,6 +1,7 @@
/* bender-tags: editor,unit,dom */

( function() {
'use strict';

var getInnerHtml = bender.tools.getInnerHtml,
getOuterHtml = function( element ) {
Expand Down Expand Up @@ -405,7 +406,7 @@
assert.areEqual( 1, node2.getIndex( true ) );
},

'getIndex - element after empty text node' : function() {
'getIndex - element after empty text node1' : function() {
var wrapper = createGetIndexTest( 'etn,el' ),
node1 = wrapper.getChild( 0 ),
node2 = wrapper.getChild( 1 );
Expand All @@ -416,7 +417,7 @@
assert.areEqual( 0, node2.getIndex( true ) );
},

'getIndex - element after empty text node' : function() {
'getIndex - element after empty text node2' : function() {
var wrapper = createGetIndexTest( 'etn,el' ),
node1 = wrapper.getChild( 0 ),
node2 = wrapper.getChild( 1 );
Expand Down Expand Up @@ -592,6 +593,53 @@
assert.isNull( null, node.getAscendant( 'i' ) );
},

test_getAscendantFuncCheck_callsNumber : function() {
var node = $( 'getAscendantFuncCheck' ),
calls = 0;

node.getAscendant( function( elem ) {
calls++;
}, true );

assert.isTrue( calls > 0, 'Should be called at least once.' );
},

test_getAscendantFuncCheck_findFirstOne : function() {
var node = $( 'getAscendantFuncCheck' ),
found = node.getAscendant( function( el ) {
return true;
}, true );

assert.areSame( node, found, 'First one match.' );
},

test_getAscendantFuncCheck_findFirstAncestor : function() {
var node = $( 'getAscendantFuncCheck' ),
found = node.getAscendant( function( el ) {
return true;
} );

assert.areSame( node.getParent(), found, 'First ancestor match.' );
},

test_getAscendantFuncCheckFindNothing : function() {
var node = $( 'getAscendantFuncCheck' ),
found = node.getAscendant( function( el ) {
return false;
} );

assert.isNull( found, 'Nothing found.' );
},

test_getAscendantFuncCheck_findFirstWithClassDeep2 : function() {
var node = $( 'getAscendantFuncCheck' ),
found = node.getAscendant( function( el ) {
return el.hasClass( 'deep2' );
}, true );

assert.areSame( $( 'deep2' ), found, 'Found element which has class deep2' );
},

test_hasAscendant : function() {
var node = $( 'getNSN1' );

Expand Down

0 comments on commit 8749148

Please sign in to comment.