]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/core/dom/node.js
69b223e68622305d9076211ee6916cfd1dd0b10b
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
7 * @fileOverview Defines the {@link CKEDITOR.dom.node} class which is the base
8 * class for classes that represent DOM nodes.
12 * Base class for classes representing DOM nodes. This constructor may return
13 * an instance of a class that inherits from this class, like
14 * {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
17 * @extends CKEDITOR.dom.domObject
18 * @constructor Creates a node class instance.
19 * @param {Object} domNode A native DOM node.
20 * @see CKEDITOR.dom.element
21 * @see CKEDITOR.dom.text
23 CKEDITOR
.dom
.node = function( domNode
) {
26 domNode
.nodeType
== CKEDITOR
.NODE_DOCUMENT
? 'document' :
27 domNode
.nodeType
== CKEDITOR
.NODE_ELEMENT
? 'element' :
28 domNode
.nodeType
== CKEDITOR
.NODE_TEXT
? 'text' :
29 domNode
.nodeType
== CKEDITOR
.NODE_COMMENT
? 'comment' :
30 domNode
.nodeType
== CKEDITOR
.NODE_DOCUMENT_FRAGMENT
? 'documentFragment' :
31 'domObject'; // Call the base constructor otherwise.
33 return new CKEDITOR
.dom
[ type
]( domNode
);
39 CKEDITOR
.dom
.node
.prototype = new CKEDITOR
.dom
.domObject();
45 * @property {Number} [=1]
48 CKEDITOR
.NODE_ELEMENT
= 1;
54 * @property {Number} [=9]
57 CKEDITOR
.NODE_DOCUMENT
= 9;
63 * @property {Number} [=3]
66 CKEDITOR
.NODE_TEXT
= 3;
72 * @property {Number} [=8]
75 CKEDITOR
.NODE_COMMENT
= 8;
78 * Document fragment node type.
81 * @property {Number} [=11]
84 CKEDITOR
.NODE_DOCUMENT_FRAGMENT
= 11;
87 * Indicates that positions of both nodes are identical (this is the same node). See {@link CKEDITOR.dom.node#getPosition}.
90 * @property {Number} [=0]
93 CKEDITOR
.POSITION_IDENTICAL
= 0;
96 * Indicates that nodes are in different (detached) trees. See {@link CKEDITOR.dom.node#getPosition}.
99 * @property {Number} [=1]
102 CKEDITOR
.POSITION_DISCONNECTED
= 1;
105 * Indicates that the context node follows the other node. See {@link CKEDITOR.dom.node#getPosition}.
108 * @property {Number} [=2]
111 CKEDITOR
.POSITION_FOLLOWING
= 2;
114 * Indicates that the context node precedes the other node. See {@link CKEDITOR.dom.node#getPosition}.
117 * @property {Number} [=4]
120 CKEDITOR
.POSITION_PRECEDING
= 4;
123 * Indicates that the context node is a descendant of the other node. See {@link CKEDITOR.dom.node#getPosition}.
126 * @property {Number} [=8]
129 CKEDITOR
.POSITION_IS_CONTAINED
= 8;
132 * Indicates that the context node contains the other node. See {@link CKEDITOR.dom.node#getPosition}.
135 * @property {Number} [=16]
138 CKEDITOR
.POSITION_CONTAINS
= 16;
140 CKEDITOR
.tools
.extend( CKEDITOR
.dom
.node
.prototype, {
142 * Makes this node a child of another element.
144 * var p = new CKEDITOR.dom.element( 'p' );
145 * var strong = new CKEDITOR.dom.element( 'strong' );
146 * strong.appendTo( p );
148 * // Result: '<p><strong></strong></p>'.
150 * @param {CKEDITOR.dom.element} element The target element to which this node will be appended.
151 * @returns {CKEDITOR.dom.element} The target element.
153 appendTo: function( element
, toStart
) {
154 element
.append( this, toStart
);
161 * **Note**: Values set by {#setCustomData} will not be available in the clone.
163 * @param {Boolean} [includeChildren=false] If `true` then all node's
164 * children will be cloned recursively.
165 * @param {Boolean} [cloneId=false] Whether ID attributes should be cloned, too.
166 * @returns {CKEDITOR.dom.node} Clone of this node.
168 clone: function( includeChildren
, cloneId
) {
169 var $clone
= this.$.cloneNode( includeChildren
);
171 // The "id" attribute should never be cloned to avoid duplication.
174 var node
= new CKEDITOR
.dom
.node( $clone
);
176 // On IE8 we need to fixed HTML5 node name, see details below.
177 if ( CKEDITOR
.env
.ie
&& CKEDITOR
.env
.version
< 9 &&
178 ( this.type
== CKEDITOR
.NODE_ELEMENT
|| this.type
== CKEDITOR
.NODE_DOCUMENT_FRAGMENT
) ) {
184 function removeIds( node
) {
185 // Reset data-cke-expando only when has been cloned (IE and only for some types of objects).
186 if ( node
[ 'data-cke-expando' ] )
187 node
[ 'data-cke-expando' ] = false;
189 if ( node
.nodeType
!= CKEDITOR
.NODE_ELEMENT
&& node
.nodeType
!= CKEDITOR
.NODE_DOCUMENT_FRAGMENT
)
192 if ( !cloneId
&& node
.nodeType
== CKEDITOR
.NODE_ELEMENT
)
193 node
.removeAttribute( 'id', false );
195 if ( includeChildren
) {
196 var childs
= node
.childNodes
;
197 for ( var i
= 0; i
< childs
.length
; i
++ )
198 removeIds( childs
[ i
] );
202 // IE8 rename HTML5 nodes by adding `:` at the begging of the tag name when the node is cloned,
203 // so `<figure>` will be `<:figure>` after 'cloneNode'. We need to fix it (http://dev.ckeditor.com/ticket/13101).
204 function renameNodes( node
) {
205 if ( node
.type
!= CKEDITOR
.NODE_ELEMENT
&& node
.type
!= CKEDITOR
.NODE_DOCUMENT_FRAGMENT
)
208 if ( node
.type
!= CKEDITOR
.NODE_DOCUMENT_FRAGMENT
) {
209 var name
= node
.getName();
210 if ( name
[ 0 ] == ':' ) {
211 node
.renameNode( name
.substring( 1 ) );
215 if ( includeChildren
) {
216 for ( var i
= 0; i
< node
.getChildCount(); i
++ )
217 renameNodes( node
.getChild( i
) );
223 * Checks if the node is preceded by any sibling.
227 hasPrevious: function() {
228 return !!this.$.previousSibling
;
232 * Checks if the node is succeeded by any sibling.
236 hasNext: function() {
237 return !!this.$.nextSibling
;
241 * Inserts this element after a node.
243 * var em = new CKEDITOR.dom.element( 'em' );
244 * var strong = new CKEDITOR.dom.element( 'strong' );
245 * strong.insertAfter( em );
247 * // Result: '<em></em><strong></strong>'
249 * @param {CKEDITOR.dom.node} node The node that will precede this element.
250 * @returns {CKEDITOR.dom.node} The node preceding this one after insertion.
252 insertAfter: function( node
) {
253 node
.$.parentNode
.insertBefore( this.$, node
.$.nextSibling
);
258 * Inserts this element before a node.
260 * var em = new CKEDITOR.dom.element( 'em' );
261 * var strong = new CKEDITOR.dom.element( 'strong' );
262 * strong.insertBefore( em );
264 * // result: '<strong></strong><em></em>'
266 * @param {CKEDITOR.dom.node} node The node that will succeed this element.
267 * @returns {CKEDITOR.dom.node} The node being inserted.
269 insertBefore: function( node
) {
270 node
.$.parentNode
.insertBefore( this.$, node
.$ );
275 * Inserts a node before this node.
277 * var em = new CKEDITOR.dom.element( 'em' );
278 * var strong = new CKEDITOR.dom.element( 'strong' );
279 * strong.insertBeforeMe( em );
281 * // result: '<em></em><strong></strong>'
283 * @param {CKEDITOR.dom.node} node The node that will preceed this element.
284 * @returns {CKEDITOR.dom.node} The node being inserted.
286 insertBeforeMe: function( node
) {
287 this.$.parentNode
.insertBefore( node
.$, this.$ );
292 * Retrieves a uniquely identifiable tree address for this node.
293 * The tree address returned is an array of integers, with each integer
294 * indicating a child index of a DOM node, starting from
295 * `document.documentElement`.
297 * For example, assuming `<body>` is the second child
298 * of `<html>` (`<head>` being the first),
299 * and we would like to address the third child under the
300 * fourth child of `<body>`, the tree address returned would be:
303 * The tree address cannot be used for finding back the DOM tree node once
304 * the DOM tree structure has been modified.
306 * @param {Boolean} [normalized=false] See {@link #getIndex}.
307 * @returns {Array} The address.
309 getAddress: function( normalized
) {
311 var $documentElement
= this.getDocument().$.documentElement
;
314 while ( node
&& node
!= $documentElement
) {
315 var parentNode
= node
.parentNode
;
318 // Get the node index. For performance, call getIndex
319 // directly, instead of creating a new node object.
320 address
.unshift( this.getIndex
.call( { $: node
}, normalized
) );
330 * Gets the document containing this element.
332 * var element = CKEDITOR.document.getById( 'example' );
333 * alert( element.getDocument().equals( CKEDITOR.document ) ); // true
335 * @returns {CKEDITOR.dom.document} The document.
337 getDocument: function() {
338 return new CKEDITOR
.dom
.document( this.$.ownerDocument
|| this.$.parentNode
.ownerDocument
);
342 * Gets the index of a node in an array of its `parent.childNodes`.
343 * Returns `-1` if a node does not have a parent or when the `normalized` argument is set to `true`
344 * and the text node is empty and will be removed during the normalization.
346 * Let us assume having the following `childNodes` array:
348 * [ emptyText, element1, text, text, element2, emptyText2 ]
350 * emptyText.getIndex() // 0
351 * emptyText.getIndex( true ) // -1
352 * element1.getIndex(); // 1
353 * element1.getIndex( true ); // 0
354 * element2.getIndex(); // 4
355 * element2.getIndex( true ); // 2
356 * emptyText2.getIndex(); // 5
357 * emptyText2.getIndex( true ); // -1
359 * @param {Boolean} normalized When `true`, adjacent text nodes are merged and empty text nodes are removed.
360 * @returns {Number} Index of a node or `-1` if a node does not have a parent or is removed during the normalization.
362 getIndex: function( normalized
) {
363 // Attention: getAddress depends on this.$
364 // getIndex is called on a plain object: { $ : node }
366 var current
= this.$,
370 if ( !this.$.parentNode
)
373 // The idea is - all empty text nodes will be virtually merged into their adjacent text nodes.
374 // If an empty text node does not have an adjacent non-empty text node we can return -1 straight away,
375 // because it and all its sibling text nodes will be merged into an empty text node and then totally ignored.
376 if ( normalized
&& current
.nodeType
== CKEDITOR
.NODE_TEXT
&& isEmpty( current
) ) {
377 var adjacent
= getAdjacentNonEmptyTextNode( current
) || getAdjacentNonEmptyTextNode( current
, true );
384 // Bypass blank node and adjacent text nodes.
385 if ( normalized
&& current
!= this.$ && current
.nodeType
== CKEDITOR
.NODE_TEXT
&& ( isNormalizing
|| isEmpty( current
) ) )
389 isNormalizing
= current
.nodeType
== CKEDITOR
.NODE_TEXT
;
391 while ( ( current
= current
.previousSibling
) );
395 function getAdjacentNonEmptyTextNode( node
, lookForward
) {
396 var sibling
= lookForward
? node
.nextSibling : node
.previousSibling
;
398 if ( !sibling
|| sibling
.nodeType
!= CKEDITOR
.NODE_TEXT
) {
402 // If found a non-empty text node, then return it.
403 // If not, then continue search.
404 return isEmpty( sibling
) ? getAdjacentNonEmptyTextNode( sibling
, lookForward
) : sibling
;
407 // Checks whether a text node is empty or is FCSeq string (which will be totally removed when normalizing).
408 function isEmpty( textNode
) {
409 return !textNode
.nodeValue
|| textNode
.nodeValue
== CKEDITOR
.dom
.selection
.FILLING_CHAR_SEQUENCE
;
416 getNextSourceNode: function( startFromSibling
, nodeType
, guard
) {
417 // If "guard" is a node, transform it in a function.
418 if ( guard
&& !guard
.call
) {
419 var guardNode
= guard
;
420 guard = function( node
) {
421 return !node
.equals( guardNode
);
425 var node
= ( !startFromSibling
&& this.getFirst
&& this.getFirst() ),
428 // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
429 // send the 'moving out' signal even we don't actually dive into.
431 if ( this.type
== CKEDITOR
.NODE_ELEMENT
&& guard
&& guard( this, true ) === false )
433 node
= this.getNext();
436 while ( !node
&& ( parent
= ( parent
|| this ).getParent() ) ) {
437 // The guard check sends the "true" paramenter to indicate that
438 // we are moving "out" of the element.
439 if ( guard
&& guard( parent
, true ) === false )
442 node
= parent
.getNext();
448 if ( guard
&& guard( node
) === false )
451 if ( nodeType
&& nodeType
!= node
.type
)
452 return node
.getNextSourceNode( false, nodeType
, guard
);
460 getPreviousSourceNode: function( startFromSibling
, nodeType
, guard
) {
461 if ( guard
&& !guard
.call
) {
462 var guardNode
= guard
;
463 guard = function( node
) {
464 return !node
.equals( guardNode
);
468 var node
= ( !startFromSibling
&& this.getLast
&& this.getLast() ),
471 // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
472 // send the 'moving out' signal even we don't actually dive into.
474 if ( this.type
== CKEDITOR
.NODE_ELEMENT
&& guard
&& guard( this, true ) === false )
476 node
= this.getPrevious();
479 while ( !node
&& ( parent
= ( parent
|| this ).getParent() ) ) {
480 // The guard check sends the "true" paramenter to indicate that
481 // we are moving "out" of the element.
482 if ( guard
&& guard( parent
, true ) === false )
485 node
= parent
.getPrevious();
491 if ( guard
&& guard( node
) === false )
494 if ( nodeType
&& node
.type
!= nodeType
)
495 return node
.getPreviousSourceNode( false, nodeType
, guard
);
501 * Gets the node that preceeds this element in its parent's child list.
503 * var element = CKEDITOR.dom.element.createFromHtml( '<div><i>prev</i><b>Example</b></div>' );
504 * var first = element.getLast().getPrev();
505 * alert( first.getName() ); // 'i'
507 * @param {Function} [evaluator] Filtering the result node.
508 * @returns {CKEDITOR.dom.node} The previous node or null if not available.
510 getPrevious: function( evaluator
) {
511 var previous
= this.$,
514 previous
= previous
.previousSibling
;
516 // Avoid returning the doc type node.
517 // http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-412266927
518 retval
= previous
&& previous
.nodeType
!= 10 && new CKEDITOR
.dom
.node( previous
);
520 while ( retval
&& evaluator
&& !evaluator( retval
) );
525 * Gets the node that follows this element in its parent's child list.
527 * var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b><i>next</i></div>' );
528 * var last = element.getFirst().getNext();
529 * alert( last.getName() ); // 'i'
531 * @param {Function} [evaluator] Filtering the result node.
532 * @returns {CKEDITOR.dom.node} The next node or null if not available.
534 getNext: function( evaluator
) {
538 next
= next
.nextSibling
;
539 retval
= next
&& new CKEDITOR
.dom
.node( next
);
541 while ( retval
&& evaluator
&& !evaluator( retval
) );
546 * Gets the parent element for this node.
548 * var node = editor.document.getBody().getFirst();
549 * var parent = node.getParent();
550 * alert( parent.getName() ); // 'body'
552 * @param {Boolean} [allowFragmentParent=false] Consider also parent node that is of
553 * fragment type {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
554 * @returns {CKEDITOR.dom.element} The parent element.
556 getParent: function( allowFragmentParent
) {
557 var parent
= this.$.parentNode
;
558 return ( parent
&& ( parent
.nodeType
== CKEDITOR
.NODE_ELEMENT
|| allowFragmentParent
&& parent
.nodeType
== CKEDITOR
.NODE_DOCUMENT_FRAGMENT
) ) ? new CKEDITOR
.dom
.node( parent
) : null;
562 * Returns an array containing node parents and the node itself. By default nodes are in _descending_ order.
564 * // Assuming that body has paragraph as the first child.
565 * var node = editor.document.getBody().getFirst();
566 * var parents = node.getParents();
567 * alert( parents[ 0 ].getName() + ',' + parents[ 2 ].getName() ); // 'html,p'
569 * @param {Boolean} [closerFirst=false] Determines the order of returned nodes.
570 * @returns {Array} Returns an array of {@link CKEDITOR.dom.node}.
572 getParents: function( closerFirst
) {
577 parents
[ closerFirst
? 'push' : 'unshift' ]( node
);
579 while ( ( node
= node
.getParent() ) );
587 getCommonAncestor: function( node
) {
588 if ( node
.equals( this ) )
591 if ( node
.contains
&& node
.contains( this ) )
594 var start
= this.contains
? this : this.getParent();
597 if ( start
.contains( node
) ) return start
;
599 while ( ( start
= start
.getParent() ) );
605 * Determines the position relation between this node and the given {@link CKEDITOR.dom.node} in the document.
606 * This node can be preceding ({@link CKEDITOR#POSITION_PRECEDING}) or following ({@link CKEDITOR#POSITION_FOLLOWING})
607 * the given node. This node can also contain ({@link CKEDITOR#POSITION_CONTAINS}) or be contained by
608 * ({@link CKEDITOR#POSITION_IS_CONTAINED}) the given node. The function returns a bitmask of constants
609 * listed above or {@link CKEDITOR#POSITION_IDENTICAL} if the given node is the same as this node.
611 * @param {CKEDITOR.dom.node} otherNode A node to check relation with.
612 * @returns {Number} Position relation between this node and given node.
614 getPosition: function( otherNode
) {
616 var $other
= otherNode
.$;
618 if ( $.compareDocumentPosition
)
619 return $.compareDocumentPosition( $other
);
621 // IE and Safari have no support for compareDocumentPosition.
624 return CKEDITOR
.POSITION_IDENTICAL
;
626 // Only element nodes support contains and sourceIndex.
627 if ( this.type
== CKEDITOR
.NODE_ELEMENT
&& otherNode
.type
== CKEDITOR
.NODE_ELEMENT
) {
629 if ( $.contains( $other
) )
630 return CKEDITOR
.POSITION_CONTAINS
+ CKEDITOR
.POSITION_PRECEDING
;
632 if ( $other
.contains( $ ) )
633 return CKEDITOR
.POSITION_IS_CONTAINED
+ CKEDITOR
.POSITION_FOLLOWING
;
636 if ( 'sourceIndex' in $ )
637 return ( $.sourceIndex
< 0 || $other
.sourceIndex
< 0 ) ? CKEDITOR
.POSITION_DISCONNECTED : ( $.sourceIndex
< $other
.sourceIndex
) ? CKEDITOR
.POSITION_PRECEDING : CKEDITOR
.POSITION_FOLLOWING
;
641 // For nodes that don't support compareDocumentPosition, contains
642 // or sourceIndex, their "address" is compared.
644 var addressOfThis
= this.getAddress(),
645 addressOfOther
= otherNode
.getAddress(),
646 minLevel
= Math
.min( addressOfThis
.length
, addressOfOther
.length
);
648 // Determinate preceding/following relationship.
649 for ( var i
= 0; i
< minLevel
; i
++ ) {
650 if ( addressOfThis
[ i
] != addressOfOther
[ i
] ) {
651 return addressOfThis
[ i
] < addressOfOther
[ i
] ? CKEDITOR
.POSITION_PRECEDING : CKEDITOR
.POSITION_FOLLOWING
;
655 // Determinate contains/contained relationship.
656 return ( addressOfThis
.length
< addressOfOther
.length
) ? CKEDITOR
.POSITION_CONTAINS
+ CKEDITOR
.POSITION_PRECEDING : CKEDITOR
.POSITION_IS_CONTAINED
+ CKEDITOR
.POSITION_FOLLOWING
;
660 * Gets the closest ancestor node of this node, specified by its name or using an evaluator function.
662 * // Suppose we have the following HTML structure:
663 * // <div id="outer"><div id="inner"><p><b>Some text</b></p></div></div>
665 * ascendant = node.getAscendant( 'div' ); // ascendant == <div id="inner">
666 * ascendant = node.getAscendant( 'b' ); // ascendant == null
667 * ascendant = node.getAscendant( 'b', true ); // ascendant == <b>
668 * ascendant = node.getAscendant( { div:1, p:1 } ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
670 * // Using custom evaluator:
671 * ascendant = node.getAscendant( function( el ) {
672 * return el.getId() == 'inner';
674 * // ascendant == <div id="inner">
677 * @param {String/Function/Object} query The name of the ancestor node to search or
678 * an object with the node names to search for or an evaluator function.
679 * @param {Boolean} [includeSelf] Whether to include the current
680 * node in the search.
681 * @returns {CKEDITOR.dom.node} The located ancestor node or `null` if not found.
683 getAscendant: function( query
, includeSelf
) {
688 if ( !includeSelf
) {
692 // Custom checker provided in an argument.
693 if ( typeof query
== 'function' ) {
694 isCustomEvaluator
= true;
697 // Predefined tag name checker.
698 isCustomEvaluator
= false;
699 evaluator = function( $ ) {
700 var name
= ( typeof $.nodeName
== 'string' ? $.nodeName
.toLowerCase() : '' );
702 return ( typeof query
== 'string' ? name
== query : name
in query
);
707 // For user provided checker we use CKEDITOR.dom.node.
708 if ( evaluator( isCustomEvaluator
? new CKEDITOR
.dom
.node( $ ) : $ ) ) {
709 return new CKEDITOR
.dom
.node( $ );
725 hasAscendant: function( name
, includeSelf
) {
732 if ( $.nodeName
&& $.nodeName
.toLowerCase() == name
)
743 move: function( target
, toStart
) {
744 target
.append( this.remove(), toStart
);
748 * Removes this node from the document DOM.
750 * var element = CKEDITOR.document.getById( 'MyElement' );
753 * @param {Boolean} [preserveChildren=false] Indicates that the children
754 * elements must remain in the document, removing only the outer tags.
756 remove: function( preserveChildren
) {
758 var parent
= $.parentNode
;
761 if ( preserveChildren
) {
762 // Move all children before the node.
764 ( child
= $.firstChild
); ) {
765 parent
.insertBefore( $.removeChild( child
), $ );
769 parent
.removeChild( $ );
778 replace: function( nodeToReplace
) {
779 this.insertBefore( nodeToReplace
);
780 nodeToReplace
.remove();
796 while ( this.getFirst
&& ( child
= this.getFirst() ) ) {
797 if ( child
.type
== CKEDITOR
.NODE_TEXT
) {
798 var trimmed
= CKEDITOR
.tools
.ltrim( child
.getText() ),
799 originalLength
= child
.getLength();
804 } else if ( trimmed
.length
< originalLength
) {
805 child
.split( originalLength
- trimmed
.length
);
807 // IE BUG: child.remove() may raise JavaScript errors here. (http://dev.ckeditor.com/ticket/81)
808 this.$.removeChild( this.$.firstChild
);
820 while ( this.getLast
&& ( child
= this.getLast() ) ) {
821 if ( child
.type
== CKEDITOR
.NODE_TEXT
) {
822 var trimmed
= CKEDITOR
.tools
.rtrim( child
.getText() ),
823 originalLength
= child
.getLength();
828 } else if ( trimmed
.length
< originalLength
) {
829 child
.split( trimmed
.length
);
831 // IE BUG: child.getNext().remove() may raise JavaScript errors here.
832 // (http://dev.ckeditor.com/ticket/81)
833 this.$.lastChild
.parentNode
.removeChild( this.$.lastChild
);
839 if ( CKEDITOR
.env
.needsBrFiller
) {
840 child
= this.$.lastChild
;
842 if ( child
&& child
.type
== 1 && child
.nodeName
.toLowerCase() == 'br' ) {
843 // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (http://dev.ckeditor.com/ticket/324).
844 child
.parentNode
.removeChild( child
);
850 * Checks if this node is read-only (should not be changed).
852 * // For the following HTML:
853 * // <b>foo</b><div contenteditable="false"><i>bar</i></div>
855 * elB.isReadOnly(); // -> false
856 * foo.isReadOnly(); // -> false
857 * elDiv.isReadOnly(); // -> true
858 * elI.isReadOnly(); // -> true
860 * This method works in two modes depending on browser support for the `element.isContentEditable` property and
861 * the value of the `checkOnlyAttributes` parameter. The `element.isContentEditable` check is faster, but it is known
862 * to malfunction in hidden or detached nodes. Additionally, when processing some detached DOM tree you may want to imitate
863 * that this happens inside an editable container (like it would happen inside the {@link CKEDITOR.editable}). To do so,
864 * you can temporarily attach this tree to an element with the `data-cke-editable` attribute and use the
865 * `checkOnlyAttributes` mode.
868 * @param {Boolean} [checkOnlyAttributes=false] If `true`, only attributes will be checked, native methods will not
869 * be used. This parameter needs to be `true` to check hidden or detached elements. Introduced in 4.5.
872 isReadOnly: function( checkOnlyAttributes
) {
874 if ( this.type
!= CKEDITOR
.NODE_ELEMENT
)
875 element
= this.getParent();
877 // Prevent Edge crash (http://dev.ckeditor.com/ticket/13609, http://dev.ckeditor.com/ticket/13919).
878 if ( CKEDITOR
.env
.edge
&& element
&& element
.is( 'textarea', 'input' ) ) {
879 checkOnlyAttributes
= true;
882 if ( !checkOnlyAttributes
&& element
&& typeof element
.$.isContentEditable
!= 'undefined' ) {
883 return !( element
.$.isContentEditable
|| element
.data( 'cke-editable' ) );
886 // Degrade for old browsers which don't support "isContentEditable", e.g. FF3
889 if ( element
.data( 'cke-editable' ) ) {
891 } else if ( element
.hasAttribute( 'contenteditable' ) ) {
892 return element
.getAttribute( 'contenteditable' ) == 'false';
895 element
= element
.getParent();
898 // Reached the root of DOM tree, no editable found.