source: trunk/phpgwapi/js/ckeditor/_source/core/dom/node.js @ 2862

Revision 2862, 16.3 KB checked in by rodsouza, 14 years ago (diff)

Ticket #663 - Atualizando e centralizando o CKEditor (v. 3.2.1)

Line 
1/*
2Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
3For licensing, see LICENSE.html or http://ckeditor.com/license
4*/
5
6/**
7 * @fileOverview Defines the {@link CKEDITOR.dom.node} class, which is the base
8 *              class for classes that represent DOM nodes.
9 */
10
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 */
22CKEDITOR.dom.node = function( domNode )
23{
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 );
31
32                        case CKEDITOR.NODE_ELEMENT :
33                                return new CKEDITOR.dom.element( domNode );
34
35                        case CKEDITOR.NODE_TEXT :
36                                return new CKEDITOR.dom.text( domNode );
37                }
38
39                // Call the base constructor.
40                CKEDITOR.dom.domObject.call( this, domNode );
41        }
42
43        return this;
44};
45
46CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
47
48/**
49 * Element node type.
50 * @constant
51 * @example
52 */
53CKEDITOR.NODE_ELEMENT = 1;
54
55/**
56 * Document node type.
57 * @constant
58 * @example
59 */
60CKEDITOR.NODE_DOCUMENT = 9;
61
62/**
63 * Text node type.
64 * @constant
65 * @example
66 */
67CKEDITOR.NODE_TEXT = 3;
68
69/**
70 * Comment node type.
71 * @constant
72 * @example
73 */
74CKEDITOR.NODE_COMMENT = 8;
75
76CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
77
78CKEDITOR.POSITION_IDENTICAL = 0;
79CKEDITOR.POSITION_DISCONNECTED = 1;
80CKEDITOR.POSITION_FOLLOWING = 2;
81CKEDITOR.POSITION_PRECEDING = 4;
82CKEDITOR.POSITION_IS_CONTAINED = 8;
83CKEDITOR.POSITION_CONTAINS = 16;
84
85CKEDITOR.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                },
105
106                clone : function( includeChildren, cloneId )
107                {
108                        var $clone = this.$.cloneNode( includeChildren );
109
110                        if ( !cloneId )
111                        {
112                                var removeIds = function( node )
113                                {
114                                        if ( node.nodeType != CKEDITOR.NODE_ELEMENT )
115                                                return;
116
117                                        node.removeAttribute( 'id', false ) ;
118                                        node.removeAttribute( '_cke_expando', false ) ;
119
120                                        var childs = node.childNodes;
121                                        for ( var i=0 ; i < childs.length ; i++ )
122                                                removeIds( childs[ i ] );
123                                };
124
125                                // The "id" attribute should never be cloned to avoid duplication.
126                                removeIds( $clone );
127                        }
128
129                        return new CKEDITOR.dom.node( $clone );
130                },
131
132                hasPrevious : function()
133                {
134                        return !!this.$.previousSibling;
135                },
136
137                hasNext : function()
138                {
139                        return !!this.$.nextSibling;
140                },
141
142                /**
143                 * Inserts this element after a node.
144                 * @param {CKEDITOR.dom.node} node The that will preceed this element.
145                 * @returns {CKEDITOR.dom.node} The node preceeding this one after
146                 *              insertion.
147                 * @example
148                 * var em = new CKEDITOR.dom.element( 'em' );
149                 * var strong = new CKEDITOR.dom.element( 'strong' );
150                 * strong.insertAfter( em );
151                 *
152                 * // result: "&lt;em&gt;&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;"
153                 */
154                insertAfter : function( node )
155                {
156                        node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
157                        return node;
158                },
159
160                /**
161                 * Inserts this element before a node.
162                 * @param {CKEDITOR.dom.node} node The that will be after this element.
163                 * @returns {CKEDITOR.dom.node} The node being inserted.
164                 * @example
165                 * var em = new CKEDITOR.dom.element( 'em' );
166                 * var strong = new CKEDITOR.dom.element( 'strong' );
167                 * strong.insertBefore( em );
168                 *
169                 * // result: "&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;"
170                 */
171                insertBefore : function( node )
172                {
173                        node.$.parentNode.insertBefore( this.$, node.$ );
174                        return node;
175                },
176
177                insertBeforeMe : function( node )
178                {
179                        this.$.parentNode.insertBefore( node.$, this.$ );
180                        return node;
181                },
182
183                /**
184                 * Retrieves a uniquely identifiable tree address for this node.
185                 * The tree address returns is an array of integers, with each integer
186                 * indicating a child index of a DOM node, starting from
187                 * document.documentElement.
188                 *
189                 * For example, assuming <body> is the second child from <html> (<head>
190                 * being the first), and we'd like to address the third child under the
191                 * fourth child of body, the tree address returned would be:
192                 * [1, 3, 2]
193                 *
194                 * The tree address cannot be used for finding back the DOM tree node once
195                 * the DOM tree structure has been modified.
196                 */
197                getAddress : function( normalized )
198                {
199                        var address = [];
200                        var $documentElement = this.getDocument().$.documentElement;
201                        var node = this.$;
202
203                        while ( node && node != $documentElement )
204                        {
205                                var parentNode = node.parentNode;
206                                var currentIndex = -1;
207
208                                if ( parentNode )
209                                {
210                                        for ( var i = 0 ; i < parentNode.childNodes.length ; i++ )
211                                        {
212                                                var candidate = parentNode.childNodes[i];
213
214                                                if ( normalized &&
215                                                                candidate.nodeType == 3 &&
216                                                                candidate.previousSibling &&
217                                                                candidate.previousSibling.nodeType == 3 )
218                                                {
219                                                        continue;
220                                                }
221
222                                                currentIndex++;
223
224                                                if ( candidate == node )
225                                                        break;
226                                        }
227
228                                        address.unshift( currentIndex );
229                                }
230
231                                node = parentNode;
232                        }
233
234                        return address;
235                },
236
237                /**
238                 * Gets the document containing this element.
239                 * @returns {CKEDITOR.dom.document} The document.
240                 * @example
241                 * var element = CKEDITOR.document.getById( 'example' );
242                 * alert( <b>element.getDocument().equals( CKEDITOR.document )</b> );  // "true"
243                 */
244                getDocument : function()
245                {
246                        var document = new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
247
248                        return (
249                        this.getDocument = function()
250                                {
251                                        return document;
252                                })();
253                },
254
255                getIndex : function()
256                {
257                        var $ = this.$;
258
259                        var currentNode = $.parentNode && $.parentNode.firstChild;
260                        var currentIndex = -1;
261
262                        while ( currentNode )
263                        {
264                                currentIndex++;
265
266                                if ( currentNode == $ )
267                                        return currentIndex;
268
269                                currentNode = currentNode.nextSibling;
270                        }
271
272                        return -1;
273                },
274
275                getNextSourceNode : function( startFromSibling, nodeType, guard )
276                {
277                        // If "guard" is a node, transform it in a function.
278                        if ( guard && !guard.call )
279                        {
280                                var guardNode = guard;
281                                guard = function( node )
282                                {
283                                        return !node.equals( guardNode );
284                                };
285                        }
286
287                        var node = ( !startFromSibling && this.getFirst && this.getFirst() ),
288                                parent;
289
290                        // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
291                        // send the 'moving out' signal even we don't actually dive into.
292                        if ( !node )
293                        {
294                                if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
295                                        return null;
296                                node = this.getNext();
297                        }
298
299                        while ( !node && ( parent = ( parent || this ).getParent() ) )
300                        {
301                                // The guard check sends the "true" paramenter to indicate that
302                                // we are moving "out" of the element.
303                                if ( guard && guard( parent, true ) === false )
304                                        return null;
305
306                                node = parent.getNext();
307                        }
308
309                        if ( !node )
310                                return null;
311
312                        if ( guard && guard( node ) === false )
313                                return null;
314
315                        if ( nodeType && nodeType != node.type )
316                                return node.getNextSourceNode( false, nodeType, guard );
317
318                        return node;
319                },
320
321                getPreviousSourceNode : function( startFromSibling, nodeType, guard )
322                {
323                        if ( guard && !guard.call )
324                        {
325                                var guardNode = guard;
326                                guard = function( node )
327                                {
328                                        return !node.equals( guardNode );
329                                };
330                        }
331
332                        var node = ( !startFromSibling && this.getLast && this.getLast() ),
333                                parent;
334
335                        // Guarding when we're skipping the current element( no children or 'startFromSibling' ).
336                        // send the 'moving out' signal even we don't actually dive into.
337                        if ( !node )
338                        {
339                                if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
340                                        return null;
341                                node = this.getPrevious();
342                        }
343
344                        while ( !node && ( parent = ( parent || this ).getParent() ) )
345                        {
346                                // The guard check sends the "true" paramenter to indicate that
347                                // we are moving "out" of the element.
348                                if ( guard && guard( parent, true ) === false )
349                                        return null;
350
351                                node = parent.getPrevious();
352                        }
353
354                        if ( !node )
355                                return null;
356
357                        if ( guard && guard( node ) === false )
358                                return null;
359
360                        if ( nodeType && node.type != nodeType )
361                                return node.getPreviousSourceNode( false, nodeType, guard );
362
363                        return node;
364                },
365
366                getPrevious : function( evaluator )
367                {
368                        var previous = this.$, retval;
369                        do
370                        {
371                                previous = previous.previousSibling;
372                                retval = previous && new CKEDITOR.dom.node( previous );
373                        }
374                        while ( retval && evaluator && !evaluator( retval ) )
375                        return retval;
376                },
377
378                /**
379                 * Gets the node that follows this element in its parent's child list.
380                 * @param {Function} evaluator Filtering the result node.
381                 * @returns {CKEDITOR.dom.node} The next node or null if not available.
382                 * @example
383                 * 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;' );
384                 * var first = <b>element.getFirst().getNext()</b>;
385                 * alert( first.getName() );  // "i"
386                 */
387                getNext : function( evaluator )
388                {
389                        var next = this.$, retval;
390                        do
391                        {
392                                next = next.nextSibling;
393                                retval = next && new CKEDITOR.dom.node( next );
394                        }
395                        while ( retval && evaluator && !evaluator( retval ) )
396                        return retval;
397                },
398
399                /**
400                 * Gets the parent element for this node.
401                 * @returns {CKEDITOR.dom.element} The parent element.
402                 * @example
403                 * var node = editor.document.getBody().getFirst();
404                 * var parent = node.<b>getParent()</b>;
405                 * alert( node.getName() );  // "body"
406                 */
407                getParent : function()
408                {
409                        var parent = this.$.parentNode;
410                        return ( parent && parent.nodeType == 1 ) ? new CKEDITOR.dom.node( parent ) : null;
411                },
412
413                getParents : function( closerFirst )
414                {
415                        var node = this;
416                        var parents = [];
417
418                        do
419                        {
420                                parents[  closerFirst ? 'push' : 'unshift' ]( node );
421                        }
422                        while ( ( node = node.getParent() ) )
423
424                        return parents;
425                },
426
427                getCommonAncestor : function( node )
428                {
429                        if ( node.equals( this ) )
430                                return this;
431
432                        if ( node.contains && node.contains( this ) )
433                                return node;
434
435                        var start = this.contains ? this : this.getParent();
436
437                        do
438                        {
439                                if ( start.contains( node ) )
440                                        return start;
441                        }
442                        while ( ( start = start.getParent() ) );
443
444                        return null;
445                },
446
447                getPosition : function( otherNode )
448                {
449                        var $ = this.$;
450                        var $other = otherNode.$;
451
452                        if ( $.compareDocumentPosition )
453                                return $.compareDocumentPosition( $other );
454
455                        // IE and Safari have no support for compareDocumentPosition.
456
457                        if ( $ == $other )
458                                return CKEDITOR.POSITION_IDENTICAL;
459
460                        // Only element nodes support contains and sourceIndex.
461                        if ( this.type == CKEDITOR.NODE_ELEMENT && otherNode.type == CKEDITOR.NODE_ELEMENT )
462                        {
463                                if ( $.contains )
464                                {
465                                        if ( $.contains( $other ) )
466                                                return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
467
468                                        if ( $other.contains( $ ) )
469                                                return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
470                                }
471
472                                if ( 'sourceIndex' in $ )
473                                {
474                                        return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED :
475                                                ( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING :
476                                                CKEDITOR.POSITION_FOLLOWING;
477                                }
478                        }
479
480                        // For nodes that don't support compareDocumentPosition, contains
481                        // or sourceIndex, their "address" is compared.
482
483                        var addressOfThis = this.getAddress(),
484                                addressOfOther = otherNode.getAddress(),
485                                minLevel = Math.min( addressOfThis.length, addressOfOther.length );
486
487                                // Determinate preceed/follow relationship.
488                                for ( var i = 0 ; i <= minLevel - 1 ; i++ )
489                                {
490                                        if ( addressOfThis[ i ] != addressOfOther[ i ] )
491                                        {
492                                                if ( i < minLevel )
493                                                {
494                                                        return addressOfThis[ i ] < addressOfOther[ i ] ?
495                                                            CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
496                                                }
497                                                break;
498                                        }
499                                }
500
501                                // Determinate contains/contained relationship.
502                                return ( addressOfThis.length < addressOfOther.length ) ?
503                                                        CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING :
504                                                        CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
505                },
506
507                /**
508                 * Gets the closes ancestor node of a specified node name.
509                 * @param {String} name Node name of ancestor node.
510                 * @param {Boolean} includeSelf (Optional) Whether to include the current
511                 * node in the calculation or not.
512                 * @returns {CKEDITOR.dom.node} Ancestor node.
513                 */
514                getAscendant : function( name, includeSelf )
515                {
516                        var $ = this.$;
517
518                        if ( !includeSelf )
519                                $ = $.parentNode;
520
521                        while ( $ )
522                        {
523                                if ( $.nodeName && $.nodeName.toLowerCase() == name )
524                                        return new CKEDITOR.dom.node( $ );
525
526                                $ = $.parentNode;
527                        }
528                        return null;
529                },
530
531                hasAscendant : function( name, includeSelf )
532                {
533                        var $ = this.$;
534
535                        if ( !includeSelf )
536                                $ = $.parentNode;
537
538                        while ( $ )
539                        {
540                                if ( $.nodeName && $.nodeName.toLowerCase() == name )
541                                        return true;
542
543                                $ = $.parentNode;
544                        }
545                        return false;
546                },
547
548                move : function( target, toStart )
549                {
550                        target.append( this.remove(), toStart );
551                },
552
553                /**
554                 * Removes this node from the document DOM.
555                 * @param {Boolean} [preserveChildren] Indicates that the children
556                 *              elements must remain in the document, removing only the outer
557                 *              tags.
558                 * @example
559                 * var element = CKEDITOR.dom.element.getById( 'MyElement' );
560                 * <b>element.remove()</b>;
561                 */
562                remove : function( preserveChildren )
563                {
564                        var $ = this.$;
565                        var parent = $.parentNode;
566
567                        if ( parent )
568                        {
569                                if ( preserveChildren )
570                                {
571                                        // Move all children before the node.
572                                        for ( var child ; ( child = $.firstChild ) ; )
573                                        {
574                                                parent.insertBefore( $.removeChild( child ), $ );
575                                        }
576                                }
577
578                                parent.removeChild( $ );
579                        }
580
581                        return this;
582                },
583
584                replace : function( nodeToReplace )
585                {
586                        this.insertBefore( nodeToReplace );
587                        nodeToReplace.remove();
588                },
589
590                trim : function()
591                {
592                        this.ltrim();
593                        this.rtrim();
594                },
595
596                ltrim : function()
597                {
598                        var child;
599                        while ( this.getFirst && ( child = this.getFirst() ) )
600                        {
601                                if ( child.type == CKEDITOR.NODE_TEXT )
602                                {
603                                        var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
604                                                originalLength = child.getLength();
605
606                                        if ( !trimmed )
607                                        {
608                                                child.remove();
609                                                continue;
610                                        }
611                                        else if ( trimmed.length < originalLength )
612                                        {
613                                                child.split( originalLength - trimmed.length );
614
615                                                // IE BUG: child.remove() may raise JavaScript errors here. (#81)
616                                                this.$.removeChild( this.$.firstChild );
617                                        }
618                                }
619                                break;
620                        }
621                },
622
623                rtrim : function()
624                {
625                        var child;
626                        while ( this.getLast && ( child = this.getLast() ) )
627                        {
628                                if ( child.type == CKEDITOR.NODE_TEXT )
629                                {
630                                        var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
631                                                originalLength = child.getLength();
632
633                                        if ( !trimmed )
634                                        {
635                                                child.remove();
636                                                continue;
637                                        }
638                                        else if ( trimmed.length < originalLength )
639                                        {
640                                                child.split( trimmed.length );
641
642                                                // IE BUG: child.getNext().remove() may raise JavaScript errors here.
643                                                // (#81)
644                                                this.$.lastChild.parentNode.removeChild( this.$.lastChild );
645                                        }
646                                }
647                                break;
648                        }
649
650                        if ( !CKEDITOR.env.ie && !CKEDITOR.env.opera )
651                        {
652                                child = this.$.lastChild;
653
654                                if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' )
655                                {
656                                        // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
657                                        child.parentNode.removeChild( child ) ;
658                                }
659                        }
660                }
661        }
662);
Note: See TracBrowser for help on using the repository browser.