source: trunk/filemanager/tp/ckeditor/_source/core/htmlparser/fragment.js @ 2000

Revision 2000, 12.7 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 * A lightweight representation of an HTML DOM structure.
8 * @constructor
9 * @example
10 */
11CKEDITOR.htmlParser.fragment = function()
12{
13        /**
14         * The nodes contained in the root of this fragment.
15         * @type Array
16         * @example
17         * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
18         * alert( fragment.children.length );  "2"
19         */
20        this.children = [];
21
22        /**
23         * Get the fragment parent. Should always be null.
24         * @type Object
25         * @default null
26         * @example
27         */
28        this.parent = null;
29
30        /** @private */
31        this._ =
32        {
33                isBlockLike : true,
34                hasInlineStarted : false
35        };
36};
37
38(function()
39{
40        // Elements which the end tag is marked as optional in the HTML 4.01 DTD
41        // (expect empty elements).
42        var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
43
44        // Block-level elements whose internal structure should be respected during
45        // parser fixing.
46        var nonBreakingBlocks = CKEDITOR.tools.extend(
47                        {table:1,ul:1,ol:1,dl:1},
48                        CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl ),
49                listBlocks = CKEDITOR.dtd.$list, listItems = CKEDITOR.dtd.$listItem;
50
51        /**
52         * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
53         * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
54         * @param {Number} [fixForBody=false] Wrap body with specified element if needed.
55         * @returns CKEDITOR.htmlParser.fragment The fragment created.
56         * @example
57         * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
58         * alert( fragment.children[0].name );  "b"
59         * alert( fragment.children[1].value );  " Text"
60         */
61        CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody )
62        {
63                var parser = new CKEDITOR.htmlParser(),
64                        html = [],
65                        fragment = new CKEDITOR.htmlParser.fragment(),
66                        pendingInline = [],
67                        currentNode = fragment,
68                    // Indicate we're inside a <pre> element, spaces should be touched differently.
69                        inPre = false,
70                        returnPoint;
71
72                function checkPending( newTagName )
73                {
74                        if ( pendingInline.length > 0 )
75                        {
76                                for ( var i = 0 ; i < pendingInline.length ; i++ )
77                                {
78                                        var pendingElement = pendingInline[ i ],
79                                                pendingName = pendingElement.name,
80                                                pendingDtd = CKEDITOR.dtd[ pendingName ],
81                                                currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
82
83                                        if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) )
84                                        {
85                                                // Get a clone for the pending element.
86                                                pendingElement = pendingElement.clone();
87
88                                                // Add it to the current node and make it the current,
89                                                // so the new element will be added inside of it.
90                                                pendingElement.parent = currentNode;
91                                                currentNode = pendingElement;
92
93                                                // Remove the pending element (back the index by one
94                                                // to properly process the next entry).
95                                                pendingInline.splice( i, 1 );
96                                                i--;
97                                        }
98                                }
99                        }
100                }
101
102                function addElement( element, target, enforceCurrent )
103                {
104                        target = target || currentNode || fragment;
105
106                        // If the target is the fragment and this element can't go inside
107                        // body (if fixForBody).
108                        if ( fixForBody && !target.type )
109                        {
110                                var elementName, realElementName;
111                                if ( element.attributes
112                                         && ( realElementName =
113                                                  element.attributes[ '_cke_real_element_type' ] ) )
114                                        elementName = realElementName;
115                                else
116                                        elementName =  element.name;
117                                if ( !( elementName in CKEDITOR.dtd.$body ) )
118                                {
119                                        var savedCurrent = currentNode;
120
121                                        // Create a <p> in the fragment.
122                                        currentNode = target;
123                                        parser.onTagOpen( fixForBody, {} );
124
125                                        // The new target now is the <p>.
126                                        target = currentNode;
127
128                                        if ( enforceCurrent )
129                                                currentNode = savedCurrent;
130                                }
131                        }
132
133                        // Rtrim empty spaces on block end boundary. (#3585)
134                        if ( element._.isBlockLike
135                                 && element.name != 'pre' )
136                        {
137
138                                var length = element.children.length,
139                                        lastChild = element.children[ length - 1 ],
140                                        text;
141                                if ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT )
142                                {
143                                        if ( !( text = CKEDITOR.tools.rtrim( lastChild.value ) ) )
144                                                element.children.length = length -1;
145                                        else
146                                                lastChild.value = text;
147                                }
148                        }
149
150                        target.add( element );
151
152                        if ( element.returnPoint )
153                        {
154                                currentNode = element.returnPoint;
155                                delete element.returnPoint;
156                        }
157                }
158
159                parser.onTagOpen = function( tagName, attributes, selfClosing )
160                {
161                        var element = new CKEDITOR.htmlParser.element( tagName, attributes );
162
163                        // "isEmpty" will be always "false" for unknown elements, so we
164                        // must force it if the parser has identified it as a selfClosing tag.
165                        if ( element.isUnknown && selfClosing )
166                                element.isEmpty = true;
167
168                        // This is a tag to be removed if empty, so do not add it immediately.
169                        if ( CKEDITOR.dtd.$removeEmpty[ tagName ] )
170                        {
171                                pendingInline.push( element );
172                                return;
173                        }
174                        else if ( tagName == 'pre' )
175                                inPre = true;
176                        else if ( tagName == 'br' && inPre )
177                        {
178                                currentNode.add( new CKEDITOR.htmlParser.text( '\n' ) );
179                                return;
180                        }
181
182                        var currentName = currentNode.name,
183                                currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );
184
185                        // If the element cannot be child of the current element.
186                        if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
187                        {
188                                // If this is the fragment node, just ignore this tag and add
189                                // its children.
190                                if ( !currentName )
191                                        return;
192
193                                var reApply = false,
194                                        addPoint;   // New position to start adding nodes.
195
196                                // Fixing malformed nested lists(#3828).
197                                if( tagName in listBlocks
198                                        && currentName in listBlocks )
199                                {
200                                        var children = currentNode.children,
201                                                lastChild = children[ children.length - 1 ];
202                                        // Move inner list into to previous list item if any.
203                                        if( lastChild && lastChild.name in listItems )
204                                                returnPoint = currentNode, addPoint = lastChild;
205                                        // Move inner list outside in the worst case.
206                                        else
207                                                addElement( currentNode, currentNode.parent );
208                                }
209                                // If the element name is the same as the current element name,
210                                // then just close the current one and append the new one to the
211                                // parent. This situation usually happens with <p>, <li>, <dt> and
212                                // <dd>, specially in IE. Do not enter in this if block in this case.
213                                else if ( tagName == currentName )
214                                {
215                                        addElement( currentNode, currentNode.parent );
216                                }
217                                else
218                                {
219                                        if ( nonBreakingBlocks[ currentName ] )
220                                        {
221                                                if ( !returnPoint )
222                                                        returnPoint = currentNode;
223                                        }
224                                        else
225                                        {
226                                                addElement( currentNode, currentNode.parent, true );
227
228                                                if ( !optionalClose[ currentName ] )
229                                                {
230                                                        // The current element is an inline element, which
231                                                        // cannot hold the new one. Put it in the pending list,
232                                                        // and try adding the new one after it.
233                                                        pendingInline.unshift( currentNode );
234                                                }
235                                        }
236
237                                        reApply = true;
238                                }
239
240                                if( addPoint )
241                                        currentNode = addPoint;
242                                // Try adding it to the return point, or the parent element.
243                                else
244                                        currentNode = currentNode.returnPoint || currentNode.parent;
245
246                                if ( reApply )
247                                {
248                                        parser.onTagOpen.apply( this, arguments );
249                                        return;
250                                }
251                        }
252
253                        checkPending( tagName );
254
255                        element.parent = currentNode;
256                        element.returnPoint = returnPoint;
257                        returnPoint = 0;
258
259                        if ( element.isEmpty )
260                                addElement( element );
261                        else
262                                currentNode = element;
263                };
264
265                parser.onTagClose = function( tagName )
266                {
267                        var index = 0,
268                                pendingAdd = [],
269                                candidate = currentNode;
270
271                        while ( candidate.type && candidate.name != tagName )
272                        {
273                                // If this is an inline element, add it to the pending list, so
274                                // it will continue after the closing tag.
275                                if ( !candidate._.isBlockLike )
276                                {
277                                        pendingInline.unshift( candidate );
278
279                                        // Increase the index, so it will not get checked again in
280                                        // the pending list check that follows.
281                                        index++;
282                                }
283
284                                // This node should be added to it's parent at this point. But,
285                                // it should happen only if the closing tag is really closing
286                                // one of the nodes. So, for now, we just cache it.
287                                pendingAdd.push( candidate );
288
289                                candidate = candidate.parent;
290                        }
291
292                        if ( candidate.type )
293                        {
294                                // Add all elements that have been found in the above loop.
295                                for ( var i = 0 ; i < pendingAdd.length ; i++ )
296                                {
297                                        var node = pendingAdd[ i ];
298                                        addElement( node, node.parent );
299                                }
300
301                                currentNode = candidate;
302
303                                if( currentNode.name == 'pre' )
304                                        inPre = false;
305
306                                addElement( candidate, candidate.parent );
307
308                                // The parent should start receiving new nodes now, except if
309                                // addElement changed the currentNode.
310                                if ( candidate == currentNode )
311                                        currentNode = currentNode.parent;
312                        }
313                        // The tag is not actually closing anything, thus we need invalidate
314                        // the pending elements.(#3862)
315                        else
316                        {
317                                pendingInline.splice( 0, index );
318                                index = 0;
319                        }
320
321                        // Check if there is any pending tag to be closed.
322                        for ( ; index < pendingInline.length ; index++ )
323                        {
324                                // If found, just remove it from the list.
325                                if ( tagName == pendingInline[ index ].name )
326                                {
327                                        pendingInline.splice( index, 1 );
328
329                                        // Decrease the index so we continue from the next one.
330                                        index--;
331                                }
332                        }
333                };
334
335                parser.onText = function( text )
336                {
337                        // Trim empty spaces at beginning of element contents except <pre>.
338                        if ( !currentNode._.hasInlineStarted && !inPre )
339                        {
340                                text = CKEDITOR.tools.ltrim( text );
341
342                                if ( text.length === 0 )
343                                        return;
344                        }
345
346                        checkPending();
347
348                        if ( fixForBody && !currentNode.type )
349                                this.onTagOpen( fixForBody, {} );
350
351                        // Shrinking consequential spaces into one single for all elements
352                        // text contents.
353                        if ( !inPre )
354                                text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' );
355
356                        currentNode.add( new CKEDITOR.htmlParser.text( text ) );
357                };
358
359                parser.onCDATA = function( cdata )
360                {
361                        currentNode.add( new CKEDITOR.htmlParser.cdata( cdata ) );
362                };
363
364                parser.onComment = function( comment )
365                {
366                        currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
367                };
368
369                // Parse it.
370                parser.parse( fragmentHtml );
371
372                // Close all pending nodes.
373                while ( currentNode.type )
374                {
375                        var parent = currentNode.parent,
376                                node = currentNode;
377
378                        if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
379                        {
380                                currentNode = parent;
381                                parser.onTagOpen( fixForBody, {} );
382                                parent = currentNode;
383                        }
384
385                        parent.add( node );
386                        currentNode = parent;
387                }
388
389                return fragment;
390        };
391
392        CKEDITOR.htmlParser.fragment.prototype =
393        {
394                /**
395                 * Adds a node to this fragment.
396                 * @param {Object} node The node to be added. It can be any of of the
397                 *              following types: {@link CKEDITOR.htmlParser.element},
398                 *              {@link CKEDITOR.htmlParser.text} and
399                 *              {@link CKEDITOR.htmlParser.comment}.
400                 * @example
401                 */
402                add : function( node )
403                {
404                        var len = this.children.length,
405                                previous = len > 0 && this.children[ len - 1 ] || null;
406
407                        if ( previous )
408                        {
409                                // If the block to be appended is following text, trim spaces at
410                                // the right of it.
411                                if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT )
412                                {
413                                        previous.value = CKEDITOR.tools.rtrim( previous.value );
414
415                                        // If we have completely cleared the previous node.
416                                        if ( previous.value.length === 0 )
417                                        {
418                                                // Remove it from the list and add the node again.
419                                                this.children.pop();
420                                                this.add( node );
421                                                return;
422                                        }
423                                }
424
425                                previous.next = node;
426                        }
427
428                        node.previous = previous;
429                        node.parent = this;
430
431                        this.children.push( node );
432
433                        this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );
434                },
435
436                /**
437                 * Writes the fragment HTML to a CKEDITOR.htmlWriter.
438                 * @param {CKEDITOR.htmlWriter} writer The writer to which write the HTML.
439                 * @example
440                 * var writer = new CKEDITOR.htmlWriter();
441                 * var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '&lt;P&gt;&lt;B&gt;Example' );
442                 * fragment.writeHtml( writer )
443                 * alert( writer.getHtml() );  "&lt;p&gt;&lt;b&gt;Example&lt;/b&gt;&lt;/p&gt;"
444                 */
445                writeHtml : function( writer, filter )
446                {
447                        for ( var i = 0, len = this.children.length ; i < len ; i++ )
448                                this.children[i].writeHtml( writer, filter );
449                }
450        };
451})();
Note: See TracBrowser for help on using the repository browser.