Nation Notes module contributed by Z&H Healthcare.
[openemr.git] / library / custom_template / ckeditor / _source / core / dom / node.js
blob2a47d34b438bdfc94ff5bcb04e51d829603089c6
1 /*
2 Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
3 For licensing, see LICENSE.html or http://ckeditor.com/license
4 */
6 /**
7  * @fileOverview Defines the {@link CKEDITOR.dom.node} class, which is the base
8  *              class for classes that represent DOM nodes.
9  */
11 /**
12  * Base class for classes representing DOM nodes. This constructor may return
13  * and instance of classes that inherits this class, like
14  * {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
15  * @augments CKEDITOR.dom.domObject
16  * @param {Object} domNode A native DOM node.
17  * @constructor
18  * @see CKEDITOR.dom.element
19  * @see CKEDITOR.dom.text
20  * @example
21  */
22 CKEDITOR.dom.node = function( domNode )
24         if ( domNode )
25         {
26                 switch ( domNode.nodeType )
27                 {
28                         // Safari don't consider document as element node type. (#3389)
29                         case CKEDITOR.NODE_DOCUMENT :
30                                 return new CKEDITOR.dom.document( domNode );
32                         case CKEDITOR.NODE_ELEMENT :
33                                 return new CKEDITOR.dom.element( domNode );
35                         case CKEDITOR.NODE_TEXT :
36                                 return new CKEDITOR.dom.text( domNode );
37                 }
39                 // Call the base constructor.
40                 CKEDITOR.dom.domObject.call( this, domNode );
41         }
43         return this;
46 CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
48 /**
49  * Element node type.
50  * @constant
51  * @example
52  */
53 CKEDITOR.NODE_ELEMENT = 1;
55 /**
56  * Document node type.
57  * @constant
58  * @example
59  */
60 CKEDITOR.NODE_DOCUMENT = 9;
62 /**
63  * Text node type.
64  * @constant
65  * @example
66  */
67 CKEDITOR.NODE_TEXT = 3;
69 /**
70  * Comment node type.
71  * @constant
72  * @example
73  */
74 CKEDITOR.NODE_COMMENT = 8;
76 CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
78 CKEDITOR.POSITION_IDENTICAL = 0;
79 CKEDITOR.POSITION_DISCONNECTED = 1;
80 CKEDITOR.POSITION_FOLLOWING = 2;
81 CKEDITOR.POSITION_PRECEDING = 4;
82 CKEDITOR.POSITION_IS_CONTAINED = 8;
83 CKEDITOR.POSITION_CONTAINS = 16;
85 CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype,
86         /** @lends CKEDITOR.dom.node.prototype */
87         {
88                 /**
89                  * Makes this node child of another element.
90                  * @param {CKEDITOR.dom.element} element The target element to which append
91                  *              this node.
92                  * @returns {CKEDITOR.dom.element} The target element.
93                  * @example
94                  * var p = new CKEDITOR.dom.element( 'p' );
95                  * var strong = new CKEDITOR.dom.element( 'strong' );
96                  * strong.appendTo( p );
97                  *
98                  * // result: "<p><strong></strong></p>"
99                  */
100                 appendTo : function( element, toStart )
101                 {
102                         element.append( this, toStart );
103                         return element;
104                 },
106                 clone : function( includeChildren, cloneId )
107                 {
108                         var $clone = this.$.cloneNode( includeChildren );
110                         var removeIds = function( node )
111                         {
112                                 if ( node.nodeType != CKEDITOR.NODE_ELEMENT )
113                                         return;
115                                 if ( !cloneId )
116                                         node.removeAttribute( 'id', false );
117                                 node.removeAttribute( 'data-cke-expando', false );
119                                 if ( includeChildren )
120                                 {
121                                         var childs = node.childNodes;
122                                         for ( var i=0; i < childs.length; i++ )
123                                                 removeIds( childs[ i ] );
124                                 }
125                         };
127                         // The "id" attribute should never be cloned to avoid duplication.
128                         removeIds( $clone );
130                         return new CKEDITOR.dom.node( $clone );
131                 },
133                 hasPrevious : function()
134                 {
135                         return !!this.$.previousSibling;
136                 },
138                 hasNext : function()
139                 {
140                         return !!this.$.nextSibling;
141                 },
143                 /**
144                  * Inserts this element after a node.
145                  * @param {CKEDITOR.dom.node} node The that will preceed this element.
146                  * @returns {CKEDITOR.dom.node} The node preceeding this one after
147                  *              insertion.
148                  * @example
149                  * var em = new CKEDITOR.dom.element( 'em' );
150                  * var strong = new CKEDITOR.dom.element( 'strong' );
151                  * strong.insertAfter( em );
152                  *
153                  * // result: "&lt;em&gt;&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;"
154                  */
155                 insertAfter : function( node )
156                 {
157                         node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
158                         return node;
159                 },
161                 /**
162                  * Inserts this element before a node.
163                  * @param {CKEDITOR.dom.node} node The that will be after this element.
164                  * @returns {CKEDITOR.dom.node} The node being inserted.
165                  * @example
166                  * var em = new CKEDITOR.dom.element( 'em' );
167                  * var strong = new CKEDITOR.dom.element( 'strong' );
168                  * strong.insertBefore( em );
169                  *
170                  * // result: "&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;"
171                  */
172                 insertBefore : function( node )
173                 {
174                         node.$.parentNode.insertBefore( this.$, node.$ );
175                         return node;
176                 },
178                 insertBeforeMe : function( node )
179                 {
180                         this.$.parentNode.insertBefore( node.$, this.$ );
181                         return node;
182                 },
184                 /**
185                  * Retrieves a uniquely identifiable tree address for this node.
186                  * The tree address returns is an array of integers, with each integer
187                  * indicating a child index of a DOM node, starting from
188                  * document.documentElement.
189                  *
190                  * For example, assuming <body> is the second child from <html> (<head>
191                  * being the first), and we'd like to address the third child under the
192                  * fourth child of body, the tree address returned would be:
193                  * [1, 3, 2]
194                  *
195                  * The tree address cannot be used for finding back the DOM tree node once
196                  * the DOM tree structure has been modified.
197                  */
198                 getAddress : function( normalized )
199                 {
200                         var address = [];
201                         var $documentElement = this.getDocument().$.documentElement;
202                         var node = this.$;
204                         while ( node && node != $documentElement )
205                         {
206                                 var parentNode = node.parentNode;
207                                 var currentIndex = -1;
209                                 if ( parentNode )
210                                 {
211                                         for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
212                                         {
213                                                 var candidate = parentNode.childNodes[i];
215                                                 if ( normalized &&
216                                                                 candidate.nodeType == 3 &&
217                                                                 candidate.previousSibling &&
218                                                                 candidate.previousSibling.nodeType == 3 )
219                                                 {
220                                                         continue;
221                                                 }
223                                                 currentIndex++;
225                                                 if ( candidate == node )
226                                                         break;
227                                         }
229                                         address.unshift( currentIndex );
230                                 }
232                                 node = parentNode;
233                         }
235                         return address;
236                 },
238                 /**
239                  * Gets the document containing this element.
240                  * @returns {CKEDITOR.dom.document} The document.
241                  * @example
242                  * var element = CKEDITOR.document.getById( 'example' );
243                  * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
244                  */
245                 getDocument : function()
246                 {
247                         return new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
248                 },
250                 getIndex : function()
251                 {
252                         var $ = this.$;
254                         var currentNode = $.parentNode && $.parentNode.firstChild;
255                         var currentIndex = -1;
257                         while ( currentNode )
258                         {
259                                 currentIndex++;
261                                 if ( currentNode == $ )
262                                         return currentIndex;
264                                 currentNode = currentNode.nextSibling;
265                         }
267                         return -1;
268                 },
270                 getNextSourceNode : function( startFromSibling, nodeType, guard )
271                 {
272                         // If "guard" is a node, transform it in a function.
273                         if ( guard && !guard.call )
274                         {
275                                 var guardNode = guard;
276                                 guard = function( node )
277                                 {
278                                         return !node.equals( guardNode );
279                                 };
280                         }
282                         var node = ( !startFromSibling && this.getFirst && this.getFirst() ),
283                                 parent;
285                         // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
286                         // send the 'moving out' signal even we don't actually dive into.
287                         if ( !node )
288                         {
289                                 if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
290                                         return null;
291                                 node = this.getNext();
292                         }
294                         while ( !node && ( parent = ( parent || this ).getParent() ) )
295                         {
296                                 // The guard check sends the "true" paramenter to indicate that
297                                 // we are moving "out" of the element.
298                                 if ( guard && guard( parent, true ) === false )
299                                         return null;
301                                 node = parent.getNext();
302                         }
304                         if ( !node )
305                                 return null;
307                         if ( guard && guard( node ) === false )
308                                 return null;
310                         if ( nodeType && nodeType != node.type )
311                                 return node.getNextSourceNode( false, nodeType, guard );
313                         return node;
314                 },
316                 getPreviousSourceNode : function( startFromSibling, nodeType, guard )
317                 {
318                         if ( guard && !guard.call )
319                         {
320                                 var guardNode = guard;
321                                 guard = function( node )
322                                 {
323                                         return !node.equals( guardNode );
324                                 };
325                         }
327                         var node = ( !startFromSibling && this.getLast && this.getLast() ),
328                                 parent;
330                         // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
331                         // send the 'moving out' signal even we don't actually dive into.
332                         if ( !node )
333                         {
334                                 if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
335                                         return null;
336                                 node = this.getPrevious();
337                         }
339                         while ( !node && ( parent = ( parent || this ).getParent() ) )
340                         {
341                                 // The guard check sends the "true" paramenter to indicate that
342                                 // we are moving "out" of the element.
343                                 if ( guard && guard( parent, true ) === false )
344                                         return null;
346                                 node = parent.getPrevious();
347                         }
349                         if ( !node )
350                                 return null;
352                         if ( guard && guard( node ) === false )
353                                 return null;
355                         if ( nodeType && node.type != nodeType )
356                                 return node.getPreviousSourceNode( false, nodeType, guard );
358                         return node;
359                 },
361                 getPrevious : function( evaluator )
362                 {
363                         var previous = this.$, retval;
364                         do
365                         {
366                                 previous = previous.previousSibling;
367                                 retval = previous && new CKEDITOR.dom.node( previous );
368                         }
369                         while ( retval && evaluator && !evaluator( retval ) )
370                         return retval;
371                 },
373                 /**
374                  * Gets the node that follows this element in its parent's child list.
375                  * @param {Function} evaluator Filtering the result node.
376                  * @returns {CKEDITOR.dom.node} The next node or null if not available.
377                  * @example
378                  * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt; &lt;i&gt;next&lt;/i&gt;&lt;/div&gt;' );
379                  * var first = <b>element.getFirst().getNext()</b>;
380                  * alert( first.getName() );  // "i"
381                  */
382                 getNext : function( evaluator )
383                 {
384                         var next = this.$, retval;
385                         do
386                         {
387                                 next = next.nextSibling;
388                                 retval = next && new CKEDITOR.dom.node( next );
389                         }
390                         while ( retval && evaluator && !evaluator( retval ) )
391                         return retval;
392                 },
394                 /**
395                  * Gets the parent element for this node.
396                  * @returns {CKEDITOR.dom.element} The parent element.
397                  * @example
398                  * var node = editor.document.getBody().getFirst();
399                  * var parent = node.<b>getParent()</b>;
400                  * alert( node.getName() );  // "body"
401                  */
402                 getParent : function()
403                 {
404                         var parent = this.$.parentNode;
405                         return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.node( parent ) : null;
406                 },
408                 getParents : function( closerFirst )
409                 {
410                         var node = this;
411                         var parents = [];
413                         do
414                         {
415                                 parents[  closerFirst ? 'push' : 'unshift' ]( node );
416                         }
417                         while ( ( node = node.getParent() ) )
419                         return parents;
420                 },
422                 getCommonAncestor : function( node )
423                 {
424                         if ( node.equals( this ) )
425                                 return this;
427                         if ( node.contains && node.contains( this ) )
428                                 return node;
430                         var start = this.contains ? this : this.getParent();
432                         do
433                         {
434                                 if ( start.contains( node ) )
435                                         return start;
436                         }
437                         while ( ( start = start.getParent() ) );
439                         return null;
440                 },
442                 getPosition : function( otherNode )
443                 {
444                         var $ = this.$;
445                         var $other = otherNode.$;
447                         if ( $.compareDocumentPosition )
448                                 return $.compareDocumentPosition( $other );
450                         // IE and Safari have no support for compareDocumentPosition.
452                         if ( $ == $other )
453                                 return CKEDITOR.POSITION_IDENTICAL;
455                         // Only element nodes support contains and sourceIndex.
456                         if ( this.type == CKEDITOR.NODE_ELEMENT && otherNode.type == CKEDITOR.NODE_ELEMENT )
457                         {
458                                 if ( $.contains )
459                                 {
460                                         if ( $.contains( $other ) )
461                                                 return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
463                                         if ( $other.contains( $ ) )
464                                                 return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
465                                 }
467                                 if ( 'sourceIndex' in $ )
468                                 {
469                                         return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED :
470                                                 ( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING :
471                                                 CKEDITOR.POSITION_FOLLOWING;
472                                 }
473                         }
475                         // For nodes that don't support compareDocumentPosition, contains
476                         // or sourceIndex, their "address" is compared.
478                         var addressOfThis = this.getAddress(),
479                                 addressOfOther = otherNode.getAddress(),
480                                 minLevel = Math.min( addressOfThis.length, addressOfOther.length );
482                                 // Determinate preceed/follow relationship.
483                                 for ( var i = 0 ; i <= minLevel - 1 ; i++ )
484                                 {
485                                         if ( addressOfThis[ i ] != addressOfOther[ i ] )
486                                         {
487                                                 if ( i < minLevel )
488                                                 {
489                                                         return addressOfThis[ i ] < addressOfOther[ i ] ?
490                                                             CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
491                                                 }
492                                                 break;
493                                         }
494                                 }
496                                 // Determinate contains/contained relationship.
497                                 return ( addressOfThis.length < addressOfOther.length ) ?
498                                                         CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING :
499                                                         CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
500                 },
502                 /**
503                  * Gets the closes ancestor node of a specified node name.
504                  * @param {String} name Node name of ancestor node.
505                  * @param {Boolean} includeSelf (Optional) Whether to include the current
506                  * node in the calculation or not.
507                  * @returns {CKEDITOR.dom.node} Ancestor node.
508                  */
509                 getAscendant : function( name, includeSelf )
510                 {
511                         var $ = this.$;
513                         if ( !includeSelf )
514                                 $ = $.parentNode;
516                         while ( $ )
517                         {
518                                 if ( $.nodeName && $.nodeName.toLowerCase() == name )
519                                         return new CKEDITOR.dom.node( $ );
521                                 $ = $.parentNode;
522                         }
523                         return null;
524                 },
526                 hasAscendant : function( name, includeSelf )
527                 {
528                         var $ = this.$;
530                         if ( !includeSelf )
531                                 $ = $.parentNode;
533                         while ( $ )
534                         {
535                                 if ( $.nodeName && $.nodeName.toLowerCase() == name )
536                                         return true;
538                                 $ = $.parentNode;
539                         }
540                         return false;
541                 },
543                 move : function( target, toStart )
544                 {
545                         target.append( this.remove(), toStart );
546                 },
548                 /**
549                  * Removes this node from the document DOM.
550                  * @param {Boolean} [preserveChildren] Indicates that the children
551                  *              elements must remain in the document, removing only the outer
552                  *              tags.
553                  * @example
554                  * var element = CKEDITOR.dom.element.getById( 'MyElement' );
555                  * <b>element.remove()</b>;
556                  */
557                 remove : function( preserveChildren )
558                 {
559                         var $ = this.$;
560                         var parent = $.parentNode;
562                         if ( parent )
563                         {
564                                 if ( preserveChildren )
565                                 {
566                                         // Move all children before the node.
567                                         for ( var child ; ( child = $.firstChild ) ; )
568                                         {
569                                                 parent.insertBefore( $.removeChild( child ), $ );
570                                         }
571                                 }
573                                 parent.removeChild( $ );
574                         }
576                         return this;
577                 },
579                 replace : function( nodeToReplace )
580                 {
581                         this.insertBefore( nodeToReplace );
582                         nodeToReplace.remove();
583                 },
585                 trim : function()
586                 {
587                         this.ltrim();
588                         this.rtrim();
589                 },
591                 ltrim : function()
592                 {
593                         var child;
594                         while ( this.getFirst && ( child = this.getFirst() ) )
595                         {
596                                 if ( child.type == CKEDITOR.NODE_TEXT )
597                                 {
598                                         var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
599                                                 originalLength = child.getLength();
601                                         if ( !trimmed )
602                                         {
603                                                 child.remove();
604                                                 continue;
605                                         }
606                                         else if ( trimmed.length < originalLength )
607                                         {
608                                                 child.split( originalLength - trimmed.length );
610                                                 // IE BUG: child.remove() may raise JavaScript errors here. (#81)
611                                                 this.$.removeChild( this.$.firstChild );
612                                         }
613                                 }
614                                 break;
615                         }
616                 },
618                 rtrim : function()
619                 {
620                         var child;
621                         while ( this.getLast && ( child = this.getLast() ) )
622                         {
623                                 if ( child.type == CKEDITOR.NODE_TEXT )
624                                 {
625                                         var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
626                                                 originalLength = child.getLength();
628                                         if ( !trimmed )
629                                         {
630                                                 child.remove();
631                                                 continue;
632                                         }
633                                         else if ( trimmed.length < originalLength )
634                                         {
635                                                 child.split( trimmed.length );
637                                                 // IE BUG: child.getNext().remove() may raise JavaScript errors here.
638                                                 // (#81)
639                                                 this.$.lastChild.parentNode.removeChild( this.$.lastChild );
640                                         }
641                                 }
642                                 break;
643                         }
645                         if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera )
646                         {
647                                 child = this.$.lastChild;
649                                 if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' )
650                                 {
651                                         // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
652                                         child.parentNode.removeChild( child ) ;
653                                 }
654                         }
655                 },
657                 /**
658                  * Checks is this node is read-only (should not be changed). It
659                  * additionaly returns the element, if any, which defines the read-only
660                  * state of this node. It may be the node itself or any of its parent
661                  * nodes.
662                  * @returns {CKEDITOR.dom.element|Boolean} An element containing
663                  *              read-only attributes or "false" if none is found.
664                  * @since 3.5
665                  * @example
666                  * // For the following HTML:
667                  * // &lt;div contenteditable="false"&gt;Some &lt;b&gt;text&lt;/b&gt;&lt;/div&gt;
668                  *
669                  * // If "ele" is the above &lt;div&gt;
670                  * ele.isReadOnly();  // the &lt;div&gt; element
671                  *
672                  * // If "ele" is the above &lt;b&gt;
673                  * ele.isReadOnly();  // the &lt;div&gt; element
674                  */
675                 isReadOnly : function()
676                 {
677                         var current = this;
678                         while( current )
679                         {
680                                 if ( current.type == CKEDITOR.NODE_ELEMENT )
681                                 {
682                                         if ( current.is( 'body' ) || !!current.data( 'cke-editable' ) )
683                                                 break;
685                                         if ( current.getAttribute( 'contentEditable' ) == 'false' )
686                                                 return current;
687                                         else if ( current.getAttribute( 'contentEditable' ) == 'true' )
688                                                 break;
689                                 }
690                                 current = current.getParent();
691                         }
693                         return false;
694                 }
695         }