source: trunk/filemanager/tp/ckeditor/_source/core/dom/element.js @ 2000

Revision 2000, 39.6 KB checked in by amuller, 14 years ago (diff)

Ticket #597 - Implementação do módulo gerenciador de arquivos

Line 
1/*
2Copyright (c) 2003-2009, 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.element} class, which
8 *              represents a DOM element.
9 */
10
11/**
12 * Represents a DOM element.
13 * @constructor
14 * @augments CKEDITOR.dom.node
15 * @param {Object|String} element A native DOM element or the element name for
16 *              new elements.
17 * @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
18 *              the element in case of element creation.
19 * @example
20 * // Create a new <span> element.
21 * var element = new CKEDITOR.dom.element( 'span' );
22 * @example
23 * // Create an element based on a native DOM element.
24 * var element = new CKEDITOR.dom.element( document.getElementById( 'myId' ) );
25 */
26CKEDITOR.dom.element = function( element, ownerDocument )
27{
28        if ( typeof element == 'string' )
29                element = ( ownerDocument ? ownerDocument.$ : document ).createElement( element );
30
31        // Call the base constructor (we must not call CKEDITOR.dom.node).
32        CKEDITOR.dom.domObject.call( this, element );
33};
34
35// PACKAGER_RENAME( CKEDITOR.dom.element )
36
37/**
38 * The the {@link CKEDITOR.dom.element} representing and element. If the
39 * element is a native DOM element, it will be transformed into a valid
40 * CKEDITOR.dom.element object.
41 * @returns {CKEDITOR.dom.element} The transformed element.
42 * @example
43 * var element = new CKEDITOR.dom.element( 'span' );
44 * alert( element == <b>CKEDITOR.dom.element.get( element )</b> );  "true"
45 * @example
46 * var element = document.getElementById( 'myElement' );
47 * alert( <b>CKEDITOR.dom.element.get( element )</b>.getName() );  e.g. "p"
48 */
49CKEDITOR.dom.element.get = function( element )
50{
51        return element && ( element.$ ? element : new CKEDITOR.dom.element( element ) );
52};
53
54CKEDITOR.dom.element.prototype = new CKEDITOR.dom.node();
55
56/**
57 * Creates an instance of the {@link CKEDITOR.dom.element} class based on the
58 * HTML representation of an element.
59 * @param {String} html The element HTML. It should define only one element in
60 *              the "root" level. The "root" element can have child nodes, but not
61 *              siblings.
62 * @returns {CKEDITOR.dom.element} The element instance.
63 * @example
64 * var element = <b>CKEDITOR.dom.element.createFromHtml( '&lt;strong class="anyclass"&gt;My element&lt;/strong&gt;' )</b>;
65 * alert( element.getName() );  // "strong"
66 */
67CKEDITOR.dom.element.createFromHtml = function( html, ownerDocument )
68{
69        var temp = new CKEDITOR.dom.element( 'div', ownerDocument );
70        temp.setHtml( html );
71
72        // When returning the node, remove it from its parent to detach it.
73        return temp.getFirst().remove();
74};
75
76CKEDITOR.dom.element.setMarker = function( database, element, name, value )
77{
78        var id = element.getCustomData( 'list_marker_id' ) ||
79                        ( element.setCustomData( 'list_marker_id', CKEDITOR.tools.getNextNumber() ).getCustomData( 'list_marker_id' ) ),
80                markerNames = element.getCustomData( 'list_marker_names' ) ||
81                        ( element.setCustomData( 'list_marker_names', {} ).getCustomData( 'list_marker_names' ) );
82        database[id] = element;
83        markerNames[name] = 1;
84
85        return element.setCustomData( name, value );
86};
87
88CKEDITOR.dom.element.clearAllMarkers = function( database )
89{
90        for ( var i in database )
91                CKEDITOR.dom.element.clearMarkers( database, database[i], true );
92};
93
94CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatabase )
95{
96        var names = element.getCustomData( 'list_marker_names' ),
97                id = element.getCustomData( 'list_marker_id' );
98        for ( var i in names )
99                element.removeCustomData( i );
100        element.removeCustomData( 'list_marker_names' );
101        if ( removeFromDatabase )
102        {
103                element.removeCustomData( 'list_marker_id' );
104                delete database[id];
105        }
106};
107
108CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
109        /** @lends CKEDITOR.dom.element.prototype */
110        {
111                /**
112                 * The node type. This is a constant value set to
113                 * {@link CKEDITOR.NODE_ELEMENT}.
114                 * @type Number
115                 * @example
116                 */
117                type : CKEDITOR.NODE_ELEMENT,
118
119                /**
120                 * Adds a CSS class to the element. It appends the class to the
121                 * already existing names.
122                 * @param {String} className The name of the class to be added.
123                 * @example
124                 * var element = new CKEDITOR.dom.element( 'div' );
125                 * element.addClass( 'classA' );  // &lt;div class="classA"&gt;
126                 * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;
127                 * element.addClass( 'classA' );  // &lt;div class="classA classB"&gt;
128                 */
129                addClass : function( className )
130                {
131                        var c = this.$.className;
132                        if ( c )
133                        {
134                                var regex = new RegExp( '(?:^|\\s)' + className + '(?:\\s|$)', '' );
135                                if ( !regex.test( c ) )
136                                        c += ' ' + className;
137                        }
138                        this.$.className = c || className;
139                },
140
141                /**
142                 * Removes a CSS class name from the elements classes. Other classes
143                 * remain untouched.
144                 * @param {String} className The name of the class to remove.
145                 * @example
146                 * var element = new CKEDITOR.dom.element( 'div' );
147                 * element.addClass( 'classA' );  // &lt;div class="classA"&gt;
148                 * element.addClass( 'classB' );  // &lt;div class="classA classB"&gt;
149                 * element.removeClass( 'classA' );  // &lt;div class="classB"&gt;
150                 * element.removeClass( 'classB' );  // &lt;div&gt;
151                 */
152                removeClass : function( className )
153                {
154                        var c = this.getAttribute( 'class' );
155                        if ( c )
156                        {
157                                var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', 'i' );
158                                if ( regex.test( c ) )
159                                {
160                                        c = c.replace( regex, '' ).replace( /^\s+/, '' );
161
162                                        if ( c )
163                                                this.setAttribute( 'class', c );
164                                        else
165                                                this.removeAttribute( 'class' );
166                                }
167                        }
168                },
169
170                hasClass : function( className )
171                {
172                        var regex = new RegExp( '(?:^|\\s+)' + className + '(?=\\s|$)', '' );
173                        return regex.test( this.getAttribute('class') );
174                },
175
176                /**
177                 * Append a node as a child of this element.
178                 * @param {CKEDITOR.dom.node|String} node The node or element name to be
179                 *              appended.
180                 * @param {Boolean} [toStart] Indicates that the element is to be
181                 *              appended at the start.
182                 * @returns {CKEDITOR.dom.node} The appended node.
183                 * @example
184                 * var p = new CKEDITOR.dom.element( 'p' );
185                 *
186                 * var strong = new CKEDITOR.dom.element( 'strong' );
187                 * <b>p.append( strong );</b>
188                 *
189                 * var em = <b>p.append( 'em' );</b>
190                 *
191                 * // result: "&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;"
192                 */
193                append : function( node, toStart )
194                {
195                        if ( typeof node == 'string' )
196                                node = this.getDocument().createElement( node );
197
198                        if ( toStart )
199                                this.$.insertBefore( node.$, this.$.firstChild );
200                        else
201                                this.$.appendChild( node.$ );
202
203                        return node;
204                },
205
206                appendHtml : function( html )
207                {
208                        if ( !this.$.childNodes.length )
209                                this.setHtml( html );
210                        else
211                        {
212                                var temp = new CKEDITOR.dom.element( 'div', this.getDocument() );
213                                temp.setHtml( html );
214                                temp.moveChildren( this );
215                        }
216                },
217
218                /**
219                 * Append text to this element.
220                 * @param {String} text The text to be appended.
221                 * @returns {CKEDITOR.dom.node} The appended node.
222                 * @example
223                 * var p = new CKEDITOR.dom.element( 'p' );
224                 * p.appendText( 'This is' );
225                 * p.appendText( ' some text' );
226                 *
227                 * // result: "&lt;p&gt;This is some text&lt;/p&gt;"
228                 */
229                appendText : function( text )
230                {
231                        if ( this.$.text != undefined )
232                                this.$.text += text;
233                        else
234                                this.append( new CKEDITOR.dom.text( text ) );
235                },
236
237                appendBogus : function()
238                {
239                        var lastChild = this.getLast() ;
240
241                        // Ignore empty/spaces text.
242                        while ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.rtrim( lastChild.getText() ) )
243                                lastChild = lastChild.getPrevious();
244                        if ( !lastChild || !lastChild.is || !lastChild.is( 'br' ) )
245                        {
246                                this.append(
247                                        CKEDITOR.env.opera ?
248                                                this.getDocument().createText('') :
249                                                this.getDocument().createElement( 'br' ) );
250                        }
251                },
252
253                /**
254                 * Breaks one of the ancestor element in the element position, moving
255                 * this element between the broken parts.
256                 * @param {CKEDITOR.dom.element} parent The anscestor element to get broken.
257                 * @example
258                 * // Before breaking:
259                 * //     <b>This <i>is some<span /> sample</i> test text</b>
260                 * // If "element" is <span /> and "parent" is <i>:
261                 * //     <b>This <i>is some</i><span /><i> sample</i> test text</b>
262                 * element.breakParent( parent );
263                 * @example
264                 * // Before breaking:
265                 * //     <b>This <i>is some<span /> sample</i> test text</b>
266                 * // If "element" is <span /> and "parent" is <b>:
267                 * //     <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b>
268                 * element.breakParent( parent );
269                 */
270                breakParent : function( parent )
271                {
272                        var range = new CKEDITOR.dom.range( this.getDocument() );
273
274                        // We'll be extracting part of this element, so let's use our
275                        // range to get the correct piece.
276                        range.setStartAfter( this );
277                        range.setEndAfter( parent );
278
279                        // Extract it.
280                        var docFrag = range.extractContents();
281
282                        // Move the element outside the broken element.
283                        range.insertNode( this.remove() );
284
285                        // Re-insert the extracted piece after the element.
286                        docFrag.insertAfterNode( this );
287                },
288
289                contains :
290                        CKEDITOR.env.ie || CKEDITOR.env.webkit ?
291                                function( node )
292                                {
293                                        var $ = this.$;
294
295                                        return node.type != CKEDITOR.NODE_ELEMENT ?
296                                                $.contains( node.getParent().$ ) :
297                                                $ != node.$ && $.contains( node.$ );
298                                }
299                        :
300                                function( node )
301                                {
302                                        return !!( this.$.compareDocumentPosition( node.$ ) & 16 );
303                                },
304
305                /**
306                 * Moves the selection focus to this element.
307                 * @example
308                 * var element = CKEDITOR.document.getById( 'myTextarea' );
309                 * <b>element.focus()</b>;
310                 */
311                focus : function()
312                {
313                        // IE throws error if the element is not visible.
314                        try
315                        {
316                                this.$.focus();
317                        }
318                        catch (e)
319                        {}
320                },
321
322                /**
323                 * Gets the inner HTML of this element.
324                 * @returns {String} The inner HTML of this element.
325                 * @example
326                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );
327                 * alert( <b>p.getHtml()</b> );  // "&lt;b&gt;Example&lt;/b&gt;"
328                 */
329                getHtml : function()
330                {
331                        return this.$.innerHTML;
332                },
333
334                getOuterHtml : function()
335                {
336                        if ( this.$.outerHTML )
337                        {
338                                // IE includes the <?xml:namespace> tag in the outerHTML of
339                                // namespaced element. So, we must strip it here. (#3341)
340                                return this.$.outerHTML.replace( /<\?[^>]*>/, '' );
341                        }
342
343                        var tmpDiv = this.$.ownerDocument.createElement( 'div' );
344                        tmpDiv.appendChild( this.$.cloneNode( true ) );
345                        return tmpDiv.innerHTML;
346                },
347
348                /**
349                 * Sets the inner HTML of this element.
350                 * @param {String} html The HTML to be set for this element.
351                 * @returns {String} The inserted HTML.
352                 * @example
353                 * var p = new CKEDITOR.dom.element( 'p' );
354                 * <b>p.setHtml( '&lt;b&gt;Inner&lt;/b&gt; HTML' );</b>
355                 *
356                 * // result: "&lt;p&gt;&lt;b&gt;Inner&lt;/b&gt; HTML&lt;/p&gt;"
357                 */
358                setHtml : function( html )
359                {
360                        return ( this.$.innerHTML = html );
361                },
362
363                /**
364                 * Sets the element contents as plain text.
365                 * @param {String} text The text to be set.
366                 * @returns {String} The inserted text.
367                 * @example
368                 * var element = new CKEDITOR.dom.element( 'div' );
369                 * element.setText( 'A > B & C < D' );
370                 * alert( element.innerHTML );  // "A &amp;gt; B &amp;amp; C &amp;lt; D"
371                 */
372                setText : function( text )
373                {
374                        CKEDITOR.dom.element.prototype.setText = ( this.$.innerText != undefined ) ?
375                                function ( text )
376                                {
377                                        return this.$.innerText = text;
378                                } :
379                                function ( text )
380                                {
381                                        return this.$.textContent = text;
382                                };
383
384                        return this.setText( text );
385                },
386
387                /**
388                 * Gets the value of an element attribute.
389                 * @function
390                 * @param {String} name The attribute name.
391                 * @returns {String} The attribute value or null if not defined.
392                 * @example
393                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input type="text" /&gt;' );
394                 * alert( <b>element.getAttribute( 'type' )</b> );  // "text"
395                 */
396                getAttribute : (function()
397                {
398                        var standard = function( name )
399                        {
400                                return this.$.getAttribute( name, 2 );
401                        };
402
403                        if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
404                        {
405                                return function( name )
406                                {
407                                        switch ( name )
408                                        {
409                                                case 'class':
410                                                        name = 'className';
411                                                        break;
412
413                                                case 'tabindex':
414                                                        var tabIndex = standard.call( this, name );
415
416                                                        // IE returns tabIndex=0 by default for all
417                                                        // elements. For those elements,
418                                                        // getAtrribute( 'tabindex', 2 ) returns 32768
419                                                        // instead. So, we must make this check to give a
420                                                        // uniform result among all browsers.
421                                                        if ( tabIndex !== 0 && this.$.tabIndex === 0 )
422                                                                tabIndex = null;
423
424                                                        return tabIndex;
425                                                        break;
426
427                                                case 'checked':
428                                                        return this.$.checked;
429                                                        break;
430
431                                                case 'style':
432                                                        // IE does not return inline styles via getAttribute(). See #2947.
433                                                        return this.$.style.cssText;
434                                        }
435
436                                        return standard.call( this, name );
437                                };
438                        }
439                        else
440                                return standard;
441                })(),
442
443                getChildren : function()
444                {
445                        return new CKEDITOR.dom.nodeList( this.$.childNodes );
446                },
447
448                /**
449                 * Gets the current computed value of one of the element CSS style
450                 * properties.
451                 * @function
452                 * @param {String} propertyName The style property name.
453                 * @returns {String} The property value.
454                 * @example
455                 * var element = new CKEDITOR.dom.element( 'span' );
456                 * alert( <b>element.getComputedStyle( 'display' )</b> );  // "inline"
457                 */
458                getComputedStyle :
459                        CKEDITOR.env.ie ?
460                                function( propertyName )
461                                {
462                                        return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ];
463                                }
464                        :
465                                function( propertyName )
466                                {
467                                        return this.getWindow().$.getComputedStyle( this.$, '' ).getPropertyValue( propertyName );
468                                },
469
470                /**
471                 * Gets the DTD entries for this element.
472                 * @returns {Object} An object containing the list of elements accepted
473                 *              by this element.
474                 */
475                getDtd : function()
476                {
477                        var dtd = CKEDITOR.dtd[ this.getName() ];
478
479                        this.getDtd = function()
480                        {
481                                return dtd;
482                        };
483
484                        return dtd;
485                },
486
487                getElementsByTag : CKEDITOR.dom.document.prototype.getElementsByTag,
488
489                /**
490                 * Gets the computed tabindex for this element.
491                 * @function
492                 * @returns {Number} The tabindex value.
493                 * @example
494                 * var element = CKEDITOR.document.getById( 'myDiv' );
495                 * alert( <b>element.getTabIndex()</b> );  // e.g. "-1"
496                 */
497                getTabIndex :
498                        CKEDITOR.env.ie ?
499                                function()
500                                {
501                                        var tabIndex = this.$.tabIndex;
502
503                                        // IE returns tabIndex=0 by default for all elements. In
504                                        // those cases we must check that the element really has
505                                        // the tabindex attribute set to zero, or it is one of
506                                        // those element that should have zero by default.
507                                        if ( tabIndex === 0 && !CKEDITOR.dtd.$tabIndex[ this.getName() ] && parseInt( this.getAttribute( 'tabindex' ), 10 ) !== 0 )
508                                                tabIndex = -1;
509
510                                                return tabIndex;
511                                }
512                        : CKEDITOR.env.webkit ?
513                                function()
514                                {
515                                        var tabIndex = this.$.tabIndex;
516
517                                        // Safari returns "undefined" for elements that should not
518                                        // have tabindex (like a div). So, we must try to get it
519                                        // from the attribute.
520                                        // https://bugs.webkit.org/show_bug.cgi?id=20596
521                                        if ( tabIndex == undefined )
522                                        {
523                                                tabIndex = parseInt( this.getAttribute( 'tabindex' ), 10 );
524
525                                                // If the element don't have the tabindex attribute,
526                                                // then we should return -1.
527                                                if ( isNaN( tabIndex ) )
528                                                        tabIndex = -1;
529                                        }
530
531                                        return tabIndex;
532                                }
533                        :
534                                function()
535                                {
536                                        return this.$.tabIndex;
537                                },
538
539                /**
540                 * Gets the text value of this element.
541                 *
542                 * Only in IE (which uses innerText), &lt;br&gt; will cause linebreaks,
543                 * and sucessive whitespaces (including line breaks) will be reduced to
544                 * a single space. This behavior is ok for us, for now. It may change
545                 * in the future.
546                 * @returns {String} The text value.
547                 * @example
548                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;Same &lt;i&gt;text&lt;/i&gt;.&lt;/div&gt;' );
549                 * alert( <b>element.getText()</b> );  // "Sample text."
550                 */
551                getText : function()
552                {
553                        return this.$.textContent || this.$.innerText || '';
554                },
555
556                /**
557                 * Gets the window object that contains this element.
558                 * @returns {CKEDITOR.dom.window} The window object.
559                 * @example
560                 */
561                getWindow : function()
562                {
563                        return this.getDocument().getWindow();
564                },
565
566                /**
567                 * Gets the value of the "id" attribute of this element.
568                 * @returns {String} The element id, or null if not available.
569                 * @example
570                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;p id="myId"&gt;&lt;/p&gt;' );
571                 * alert( <b>element.getId()</b> );  // "myId"
572                 */
573                getId : function()
574                {
575                        return this.$.id || null;
576                },
577
578                /**
579                 * Gets the value of the "name" attribute of this element.
580                 * @returns {String} The element name, or null if not available.
581                 * @example
582                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;input name="myName"&gt;&lt;/input&gt;' );
583                 * alert( <b>element.getNameAtt()</b> );  // "myName"
584                 */
585                getNameAtt : function()
586                {
587                        return this.$.name || null;
588                },
589
590                /**
591                 * Gets the element name (tag name). The returned name is guaranteed to
592                 * be always full lowercased.
593                 * @returns {String} The element name.
594                 * @example
595                 * var element = new CKEDITOR.dom.element( 'span' );
596                 * alert( <b>element.getName()</b> );  // "span"
597                 */
598                getName : function()
599                {
600                        // Cache the lowercased name inside a closure.
601                        var nodeName = this.$.nodeName.toLowerCase();
602
603                        if ( CKEDITOR.env.ie )
604                        {
605                                var scopeName = this.$.scopeName;
606                                if ( scopeName != 'HTML' )
607                                        nodeName = scopeName.toLowerCase() + ':' + nodeName;
608                        }
609
610                        return (
611                        /** @ignore */
612                        this.getName = function()
613                                {
614                                        return nodeName;
615                                })();
616                },
617
618                /**
619                 * Gets the value set to this element. This value is usually available
620                 * for form field elements.
621                 * @returns {String} The element value.
622                 */
623                getValue : function()
624                {
625                        return this.$.value;
626                },
627
628                /**
629                 * Gets the first child node of this element.
630                 * @returns {CKEDITOR.dom.node} The first child node or null if not
631                 *              available.
632                 * @example
633                 * var element = CKEDITOR.dom.element.createFromHtml( '&lt;div&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/div&gt;' );
634                 * var first = <b>element.getFirst()</b>;
635                 * alert( first.getName() );  // "b"
636                 */
637                getFirst : function()
638                {
639                        var $ = this.$.firstChild;
640                        return $ ? new CKEDITOR.dom.node( $ ) : null;
641                },
642
643                /**
644                 * @param {Function} evaluator Filtering the result node.
645                 */
646                getLast : function( evaluator )
647                {
648                        var last = this.$.lastChild,
649                                retval = last && new CKEDITOR.dom.node( last );
650                        if ( retval && evaluator && !evaluator( retval ) )
651                                retval = retval.getPrevious( evaluator );
652
653                        return retval;
654                },
655
656                getStyle : function( name )
657                {
658                        return this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ];
659                },
660
661                /**
662                 * Checks if the element name matches one or more names.
663                 * @param {String} name[,name[,...]] One or more names to be checked.
664                 * @returns {Boolean} true if the element name matches any of the names.
665                 * @example
666                 * var element = new CKEDITOR.element( 'span' );
667                 * alert( <b>element.is( 'span' )</b> );  "true"
668                 * alert( <b>element.is( 'p', 'span' )</b> );  "true"
669                 * alert( <b>element.is( 'p' )</b> );  "false"
670                 * alert( <b>element.is( 'p', 'div' )</b> );  "false"
671                 */
672                is : function()
673                {
674                        var name = this.getName();
675                        for ( var i = 0 ; i < arguments.length ; i++ )
676                        {
677                                if ( arguments[ i ] == name )
678                                        return true;
679                        }
680                        return false;
681                },
682
683                isEditable : function()
684                {
685                        // Get the element name.
686                        var name = this.getName();
687
688                        // Get the element DTD (defaults to span for unknown elements).
689                        var dtd = !CKEDITOR.dtd.$nonEditable[ name ]
690                                                && ( CKEDITOR.dtd[ name ] || CKEDITOR.dtd.span );
691
692                        // In the DTD # == text node.
693                        return ( dtd && dtd['#'] );
694                },
695
696                isIdentical : function( otherElement )
697                {
698                        if ( this.getName() != otherElement.getName() )
699                                return false;
700
701                        var thisAttribs = this.$.attributes,
702                                otherAttribs = otherElement.$.attributes;
703
704                        var thisLength = thisAttribs.length,
705                                otherLength = otherAttribs.length;
706
707                        if ( !CKEDITOR.env.ie && thisLength != otherLength )
708                                return false;
709
710                        for ( var i = 0 ; i < thisLength ; i++ )
711                        {
712                                var attribute = thisAttribs[ i ];
713
714                                if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != otherElement.getAttribute( attribute.nodeName ) )
715                                        return false;
716                        }
717
718                        // For IE, we have to for both elements, because it's difficult to
719                        // know how the atttibutes collection is organized in its DOM.
720                        if ( CKEDITOR.env.ie )
721                        {
722                                for ( i = 0 ; i < otherLength ; i++ )
723                                {
724                                        attribute = otherAttribs[ i ];
725
726                                        if ( ( !CKEDITOR.env.ie || ( attribute.specified && attribute.nodeName != '_cke_expando' ) ) && attribute.nodeValue != thisAttribs.getAttribute( attribute.nodeName ) )
727                                                return false;
728                                }
729                        }
730
731                        return true;
732                },
733
734                /**
735                 * Checks if this element is visible. May not work if the element is
736                 * child of an element with visibility set to "hidden", but works well
737                 * on the great majority of cases.
738                 * @return {Boolean} True if the element is visible.
739                 */
740                isVisible : function()
741                {
742                        return this.$.offsetWidth && ( this.$.style.visibility != 'hidden' );
743                },
744
745                /**
746                 * Indicates that the element has defined attributes.
747                 * @returns {Boolean} True if the element has attributes.
748                 * @example
749                 * var element = CKEDITOR.dom.element.createFromHtml( '<div title="Test">Example</div>' );
750                 * alert( <b>element.hasAttributes()</b> );  "true"
751                 * @example
752                 * var element = CKEDITOR.dom.element.createFromHtml( '<div>Example</div>' );
753                 * alert( <b>element.hasAttributes()</b> );  "false"
754                 */
755                hasAttributes :
756                        CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) ?
757                                function()
758                                {
759                                        var attributes = this.$.attributes;
760
761                                        for ( var i = 0 ; i < attributes.length ; i++ )
762                                        {
763                                                var attribute = attributes[i];
764
765                                                switch ( attribute.nodeName )
766                                                {
767                                                        case 'class' :
768                                                                // IE has a strange bug. If calling removeAttribute('className'),
769                                                                // the attributes collection will still contain the "class"
770                                                                // attribute, which will be marked as "specified", even if the
771                                                                // outerHTML of the element is not displaying the class attribute.
772                                                                // Note : I was not able to reproduce it outside the editor,
773                                                                // but I've faced it while working on the TC of #1391.
774                                                                if ( this.getAttribute( 'class' ) )
775                                                                        return true;
776
777                                                        // Attributes to be ignored.
778                                                        case '_cke_expando' :
779                                                                continue;
780
781                                                        /*jsl:fallthru*/
782
783                                                        default :
784                                                                if ( attribute.specified )
785                                                                        return true;
786                                                }
787                                        }
788
789                                        return false;
790                                }
791                        :
792                                function()
793                                {
794                                        var attributes = this.$.attributes;
795                                        return ( attributes.length > 1 || ( attributes.length == 1 && attributes[0].nodeName != '_cke_expando' ) );
796                                },
797
798                /**
799                 * Indicates whether a specified attribute is defined for this element.
800                 * @returns {Boolean} True if the specified attribute is defined.
801                 * @param (String) name The attribute name.
802                 * @example
803                 */
804                hasAttribute : function( name )
805                {
806                        var $attr = this.$.attributes.getNamedItem( name );
807                        return !!( $attr && $attr.specified );
808                },
809
810                /**
811                 * Hides this element (display:none).
812                 * @example
813                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
814                 * <b>element.hide()</b>;
815                 */
816                hide : function()
817                {
818                        this.setStyle( 'display', 'none' );
819                },
820
821                moveChildren : function( target, toStart )
822                {
823                        var $ = this.$;
824                        target = target.$;
825
826                        if ( $ == target )
827                                return;
828
829                        var child;
830
831                        if ( toStart )
832                        {
833                                while ( ( child = $.lastChild ) )
834                                        target.insertBefore( $.removeChild( child ), target.firstChild );
835                        }
836                        else
837                        {
838                                while ( ( child = $.firstChild ) )
839                                        target.appendChild( $.removeChild( child ) );
840                        }
841                },
842
843                /**
844                 * Shows this element (display it).
845                 * @example
846                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
847                 * <b>element.show()</b>;
848                 */
849                show : function()
850                {
851                        this.setStyles(
852                                {
853                                        display : '',
854                                        visibility : ''
855                                });
856                },
857
858                /**
859                 * Sets the value of an element attribute.
860                 * @param {String} name The name of the attribute.
861                 * @param {String} value The value to be set to the attribute.
862                 * @function
863                 * @returns {CKEDITOR.dom.element} This element instance.
864                 * @example
865                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
866                 * <b>element.setAttribute( 'class', 'myClass' )</b>;
867                 * <b>element.setAttribute( 'title', 'This is an example' )</b>;
868                 */
869                setAttribute : (function()
870                {
871                        var standard = function( name, value )
872                        {
873                                this.$.setAttribute( name, value );
874                                return this;
875                        };
876
877                        if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
878                        {
879                                return function( name, value )
880                                {
881                                        if ( name == 'class' )
882                                                this.$.className = value;
883                                        else if ( name == 'style' )
884                                                this.$.style.cssText = value;
885                                        else if ( name == 'tabindex' )  // Case sensitive.
886                                                this.$.tabIndex = value;
887                                        else if ( name == 'checked' )
888                                                this.$.checked = value;
889                                        else
890                                                standard.apply( this, arguments );
891                                        return this;
892                                };
893                        }
894                        else
895                                return standard;
896                })(),
897
898                /**
899                 * Sets the value of several element attributes.
900                 * @param {Object} attributesPairs An object containing the names and
901                 *              values of the attributes.
902                 * @returns {CKEDITOR.dom.element} This element instance.
903                 * @example
904                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
905                 * <b>element.setAttributes({
906                 *     'class' : 'myClass',
907                 *     'title' : 'This is an example' })</b>;
908                 */
909                setAttributes : function( attributesPairs )
910                {
911                        for ( var name in attributesPairs )
912                                this.setAttribute( name, attributesPairs[ name ] );
913                        return this;
914                },
915
916                /**
917                 * Sets the element value. This function is usually used with form
918                 * field element.
919                 * @param {String} value The element value.
920                 * @returns {CKEDITOR.dom.element} This element instance.
921                 */
922                setValue : function( value )
923                {
924                        this.$.value = value;
925                        return this;
926                },
927
928                /**
929                 * Removes an attribute from the element.
930                 * @param {String} name The attribute name.
931                 * @function
932                 * @example
933                 * var element = CKEDITOR.dom.element.createFromHtml( '<div class="classA"></div>' );
934                 * element.removeAttribute( 'class' );
935                 */
936                removeAttribute : (function()
937                {
938                        var standard = function( name )
939                        {
940                                this.$.removeAttribute( name );
941                        };
942
943                        if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
944                        {
945                                return function( name )
946                                {
947                                        if ( name == 'class' )
948                                                name = 'className';
949                                        else if ( name == 'tabindex' )
950                                                name = 'tabIndex';
951                                        standard.call( this, name );
952                                };
953                        }
954                        else
955                                return standard;
956                })(),
957
958                removeAttributes : function ( attributes )
959                {
960                        for ( var i = 0 ; i < attributes.length ; i++ )
961                                this.removeAttribute( attributes[ i ] );
962                },
963
964                /**
965                 * Removes a style from the element.
966                 * @param {String} name The style name.
967                 * @function
968                 * @example
969                 * var element = CKEDITOR.dom.element.createFromHtml( '<div style="display:none"></div>' );
970                 * element.removeStyle( 'display' );
971                 */
972                removeStyle : function( name )
973                {
974                        if ( this.$.style.removeAttribute )
975                                this.$.style.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) );
976                        else
977                                this.setStyle( name, '' );
978
979                        if ( !this.$.style.cssText )
980                                this.removeAttribute( 'style' );
981                },
982
983                /**
984                 * Sets the value of an element style.
985                 * @param {String} name The name of the style. The CSS naming notation
986                 *              must be used (e.g. "background-color").
987                 * @param {String} value The value to be set to the style.
988                 * @returns {CKEDITOR.dom.element} This element instance.
989                 * @example
990                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
991                 * <b>element.setStyle( 'background-color', '#ff0000' )</b>;
992                 * <b>element.setStyle( 'margin-top', '10px' )</b>;
993                 * <b>element.setStyle( 'float', 'right' )</b>;
994                 */
995                setStyle : function( name, value )
996                {
997                        this.$.style[ CKEDITOR.tools.cssStyleToDomStyle( name ) ] = value;
998                        return this;
999                },
1000
1001                /**
1002                 * Sets the value of several element styles.
1003                 * @param {Object} stylesPairs An object containing the names and
1004                 *              values of the styles.
1005                 * @returns {CKEDITOR.dom.element} This element instance.
1006                 * @example
1007                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
1008                 * <b>element.setStyles({
1009                 *     'position' : 'absolute',
1010                 *     'float' : 'right' })</b>;
1011                 */
1012                setStyles : function( stylesPairs )
1013                {
1014                        for ( var name in stylesPairs )
1015                                this.setStyle( name, stylesPairs[ name ] );
1016                        return this;
1017                },
1018
1019                /**
1020                 * Sets the opacity of an element.
1021                 * @param {Number} opacity A number within the range [0.0, 1.0].
1022                 * @example
1023                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
1024                 * <b>element.setOpacity( 0.75 )</b>;
1025                 */
1026                setOpacity : function( opacity )
1027                {
1028                        if ( CKEDITOR.env.ie )
1029                        {
1030                                opacity = Math.round( opacity * 100 );
1031                                this.setStyle( 'filter', opacity >= 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' );
1032                        }
1033                        else
1034                                this.setStyle( 'opacity', opacity );
1035                },
1036
1037                /**
1038                 * Makes the element and its children unselectable.
1039                 * @function
1040                 * @example
1041                 * var element = CKEDITOR.dom.element.getById( 'myElement' );
1042                 * element.unselectable();
1043                 */
1044                unselectable :
1045                        CKEDITOR.env.gecko ?
1046                                function()
1047                                {
1048                                        this.$.style.MozUserSelect = 'none';
1049                                }
1050                        : CKEDITOR.env.webkit ?
1051                                function()
1052                                {
1053                                        this.$.style.KhtmlUserSelect = 'none';
1054                                }
1055                        :
1056                                function()
1057                                {
1058                                        if ( CKEDITOR.env.ie || CKEDITOR.env.opera )
1059                                        {
1060                                                var element = this.$,
1061                                                        e,
1062                                                        i = 0;
1063
1064                                                element.unselectable = 'on';
1065
1066                                                while ( ( e = element.all[ i++ ] ) )
1067                                                {
1068                                                        switch ( e.tagName.toLowerCase() )
1069                                                        {
1070                                                                case 'iframe' :
1071                                                                case 'textarea' :
1072                                                                case 'input' :
1073                                                                case 'select' :
1074                                                                        /* Ignore the above tags */
1075                                                                        break;
1076                                                                default :
1077                                                                        e.unselectable = 'on';
1078                                                        }
1079                                                }
1080                                        }
1081                                },
1082
1083                getPositionedAncestor : function()
1084                {
1085                        var current = this;
1086                        while ( current.getName() != 'html' )
1087                        {
1088                                if ( current.getComputedStyle( 'position' ) != 'static' )
1089                                        return current;
1090
1091                                current = current.getParent();
1092                        }
1093                        return null;
1094                },
1095
1096                getDocumentPosition : function( refDocument )
1097                {
1098                        var x = 0, y = 0,
1099                                body = this.getDocument().getBody(),
1100                                quirks = this.getDocument().$.compatMode == 'BackCompat';
1101
1102                        var doc = this.getDocument();
1103
1104                        if ( document.documentElement[ "getBoundingClientRect" ] )
1105                        {
1106                                var box  = this.$.getBoundingClientRect(),
1107                                        $doc = doc.$,
1108                                        $docElem = $doc.documentElement;
1109
1110                                var clientTop = $docElem.clientTop || body.$.clientTop || 0,
1111                                        clientLeft = $docElem.clientLeft || body.$.clientLeft || 0,
1112                                        needAdjustScrollAndBorders = true;
1113
1114                                /*
1115                                 * #3804: getBoundingClientRect() works differently on IE and non-IE
1116                                 * browsers, regarding scroll positions.
1117                                 *
1118                                 * On IE, the top position of the <html> element is always 0, no matter
1119                                 * how much you scrolled down.
1120                                 *
1121                                 * On other browsers, the top position of the <html> element is negative
1122                                 * scrollTop.
1123                                 */
1124                                if ( CKEDITOR.env.ie )
1125                                {
1126                                        var inDocElem = doc.getDocumentElement().contains( this ),
1127                                                inBody = doc.getBody().contains( this );
1128
1129                                        needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem );
1130                                }
1131
1132                                if ( needAdjustScrollAndBorders )
1133                                {
1134                                        x = box.left + ( !quirks && $docElem.scrollLeft || body.$.scrollLeft );
1135                                        x -= clientLeft;
1136                                        y = box.top  + ( !quirks && $docElem.scrollTop || body.$.scrollTop );
1137                                        y -= clientTop;
1138                                }
1139                        }
1140                        else
1141                        {
1142                                var current = this, previous = null, offsetParent;
1143                                while ( current && !( current.getName() == 'body' || current.getName() == 'html' ) )
1144                                {
1145                                        x += current.$.offsetLeft - current.$.scrollLeft;
1146                                        y += current.$.offsetTop - current.$.scrollTop;
1147
1148                                        // Opera includes clientTop|Left into offsetTop|Left.
1149                                        if ( !current.equals( this ) )
1150                                        {
1151                                                x += ( current.$.clientLeft || 0 );
1152                                                y += ( current.$.clientTop || 0 );
1153                                        }
1154
1155                                        var scrollElement = previous;
1156                                        while ( scrollElement && !scrollElement.equals( current ) )
1157                                        {
1158                                                x -= scrollElement.$.scrollLeft;
1159                                                y -= scrollElement.$.scrollTop;
1160                                                scrollElement = scrollElement.getParent();
1161                                        }
1162
1163                                        previous = current;
1164                                        current = ( offsetParent = current.$.offsetParent ) ?
1165                                                  new CKEDITOR.dom.element( offsetParent ) : null;
1166                                }
1167                        }
1168
1169                        if ( refDocument )
1170                        {
1171                                var currentWindow = this.getWindow(),
1172                                        refWindow = refDocument.getWindow();
1173
1174                                if ( !currentWindow.equals( refWindow ) && currentWindow.$.frameElement )
1175                                {
1176                                        var iframePosition = ( new CKEDITOR.dom.element( currentWindow.$.frameElement ) ).getDocumentPosition( refDocument );
1177
1178                                        x += iframePosition.x;
1179                                        y += iframePosition.y;
1180                                }
1181                        }
1182
1183                        if ( !document.documentElement[ "getBoundingClientRect" ] )
1184                        {
1185                                // In Firefox, we'll endup one pixel before the element positions,
1186                                // so we must add it here.
1187                                if ( CKEDITOR.env.gecko && !quirks )
1188                                {
1189                                        x += this.$.clientLeft ? 1 : 0;
1190                                        y += this.$.clientTop ? 1 : 0;
1191                                }
1192                        }
1193
1194                        return { x : x, y : y };
1195                },
1196
1197                scrollIntoView : function( alignTop )
1198                {
1199                        // Get the element window.
1200                        var win = this.getWindow(),
1201                                winHeight = win.getViewPaneSize().height;
1202
1203                        // Starts from the offset that will be scrolled with the negative value of
1204                        // the visible window height.
1205                        var offset = winHeight * -1;
1206
1207                        // Append the view pane's height if align to top.
1208                        // Append element height if we are aligning to the bottom.
1209                        if ( alignTop )
1210                                offset += winHeight;
1211                        else
1212                        {
1213                                offset += this.$.offsetHeight || 0;
1214
1215                                // Consider the margin in the scroll, which is ok for our current needs, but
1216                                // needs investigation if we will be using this function in other places.
1217                                offset += parseInt( this.getComputedStyle( 'marginBottom' ) || 0, 10 ) || 0;
1218                        }
1219
1220                        // Append the offsets for the entire element hierarchy.
1221                        var elementPosition = this.getDocumentPosition();
1222                        offset += elementPosition.y;
1223
1224                        // offset value might be out of range(nagative), fix it(#3692).
1225                        offset = offset < 0 ? 0 : offset;
1226
1227                        // Scroll the window to the desired position, if not already visible(#3795).
1228                        var currentScroll = win.getScrollPosition().y;
1229                        if ( offset > currentScroll || offset < currentScroll - winHeight )
1230                                win.$.scrollTo( 0, offset );
1231                },
1232
1233                setState : function( state )
1234                {
1235                        switch ( state )
1236                        {
1237                                case CKEDITOR.TRISTATE_ON :
1238                                        this.addClass( 'cke_on' );
1239                                        this.removeClass( 'cke_off' );
1240                                        this.removeClass( 'cke_disabled' );
1241                                        break;
1242                                case CKEDITOR.TRISTATE_DISABLED :
1243                                        this.addClass( 'cke_disabled' );
1244                                        this.removeClass( 'cke_off' );
1245                                        this.removeClass( 'cke_on' );
1246                                        break;
1247                                default :
1248                                        this.addClass( 'cke_off' );
1249                                        this.removeClass( 'cke_on' );
1250                                        this.removeClass( 'cke_disabled' );
1251                                        break;
1252                        }
1253                },
1254
1255                /**
1256                 * Returns the inner document of this IFRAME element.
1257                 * @returns {CKEDITOR.dom.document} The inner document.
1258                 */
1259                getFrameDocument : function()
1260                {
1261                        var $ = this.$;
1262
1263                        try
1264                        {
1265                                // In IE, with custom document.domain, it may happen that
1266                                // the iframe is not yet available, resulting in "Access
1267                                // Denied" for the following property access.
1268                                $.contentWindow.document;
1269                        }
1270                        catch ( e )
1271                        {
1272                                // Trick to solve this issue, forcing the iframe to get ready
1273                                // by simply setting its "src" property.
1274                                $.src = $.src;
1275
1276                                // In IE6 though, the above is not enough, so we must pause the
1277                                // execution for a while, giving it time to think.
1278                                if ( CKEDITOR.env.ie && CKEDITOR.env.version < 7 )
1279                                {
1280                                        window.showModalDialog(
1281                                                'javascript:document.write("' +
1282                                                        '<script>' +
1283                                                                'window.setTimeout(' +
1284                                                                        'function(){window.close();}' +
1285                                                                        ',50);' +
1286                                                        '</script>")' );
1287                                }
1288                        }
1289
1290                        return $ && new CKEDITOR.dom.document( $.contentWindow.document );
1291                },
1292
1293                /**
1294                 * Copy all the attributes from one node to the other, kinda like a clone
1295                 * skipAttributes is an object with the attributes that must NOT be copied.
1296                 * @param {CKEDITOR.dom.element} dest The destination element.
1297                 * @param {Object} skipAttributes A dictionary of attributes to skip.
1298                 * @example
1299                 */
1300                copyAttributes : function( dest, skipAttributes )
1301                {
1302                        var attributes = this.$.attributes;
1303                        skipAttributes = skipAttributes || {};
1304
1305                        for ( var n = 0 ; n < attributes.length ; n++ )
1306                        {
1307                                var attribute = attributes[n];
1308
1309                                // IE BUG: value attribute is never specified even if it exists.
1310                                if ( attribute.specified ||
1311                                  ( CKEDITOR.env.ie && attribute.nodeValue && attribute.nodeName.toLowerCase() == 'value' ) )
1312                                {
1313                                        var attrName = attribute.nodeName;
1314                                        // We can set the type only once, so do it with the proper value, not copying it.
1315                                        if ( attrName in skipAttributes )
1316                                                continue;
1317
1318                                        var attrValue = this.getAttribute( attrName );
1319                                        if ( attrValue === null )
1320                                                attrValue = attribute.nodeValue;
1321
1322                                        dest.setAttribute( attrName, attrValue );
1323                                }
1324                        }
1325
1326                        // The style:
1327                        if ( this.$.style.cssText !== '' )
1328                                dest.$.style.cssText = this.$.style.cssText;
1329                },
1330
1331                /**
1332                 * Changes the tag name of the current element.
1333                 * @param {String} newTag The new tag for the element.
1334                 */
1335                renameNode : function( newTag )
1336                {
1337                        // If it's already correct exit here.
1338                        if ( this.getName() == newTag )
1339                                return;
1340
1341                        var doc = this.getDocument();
1342
1343                        // Create the new node.
1344                        var newNode = new CKEDITOR.dom.element( newTag, doc );
1345
1346                        // Copy all attributes.
1347                        this.copyAttributes( newNode );
1348
1349                        // Move children to the new node.
1350                        this.moveChildren( newNode );
1351
1352                        // Replace the node.
1353                        this.$.parentNode.replaceChild( newNode.$, this.$ );
1354                        newNode.$._cke_expando = this.$._cke_expando;
1355                        this.$ = newNode.$;
1356                },
1357
1358                /**
1359                 * Gets a DOM tree descendant under the current node.
1360                 * @param {Array|Number} indices The child index or array of child indices under the node.
1361                 * @returns {CKEDITOR.dom.node} The specified DOM child under the current node. Null if child does not exist.
1362                 * @example
1363                 * var strong = p.getChild(0);
1364                 */
1365                getChild : function( indices )
1366                {
1367                        var rawNode = this.$;
1368
1369                        if ( !indices.slice )
1370                                rawNode = rawNode.childNodes[ indices ];
1371                        else
1372                        {
1373                                while ( indices.length > 0 && rawNode )
1374                                        rawNode = rawNode.childNodes[ indices.shift() ];
1375                        }
1376
1377                        return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
1378                },
1379
1380                getChildCount : function()
1381                {
1382                        return this.$.childNodes.length;
1383                },
1384
1385                disableContextMenu : function()
1386                {
1387                        this.on( 'contextmenu', function( event )
1388                                {
1389                                        // Cancel the browser context menu.
1390                                        if ( !event.data.getTarget().hasClass( 'cke_enable_context_menu' ) )
1391                                                event.data.preventDefault();
1392                                } );
1393                }
1394        });
Note: See TracBrowser for help on using the repository browser.