source: trunk/phpgwapi/js/ckeditor/_source/plugins/htmldataprocessor/plugin.js @ 2862

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

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

Line 
1/*
2Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
3For licensing, see LICENSE.html or http://ckeditor.com/license
4*/
5
6(function()
7{
8        // Regex to scan for   at the end of blocks, which are actually placeholders.
9        // Safari transforms the   to \xa0. (#4172)
10        var tailNbspRegex = /^[\t\r\n ]*(?: |\xa0)$/;
11
12        var protectedSourceMarker = '{cke_protected}';
13
14        // Return the last non-space child node of the block (#4344).
15        function lastNoneSpaceChild( block )
16        {
17                var lastIndex = block.children.length,
18                        last = block.children[ lastIndex - 1 ];
19                while (  last && last.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( last.value ) )
20                        last = block.children[ --lastIndex ];
21                return last;
22        }
23
24        function trimFillers( block, fromSource )
25        {
26                // If the current node is a block, and if we're converting from source or
27                // we're not in IE then search for and remove any tailing BR node.
28                //
29                // Also, any   at the end of blocks are fillers, remove them as well.
30                // (#2886)
31                var children = block.children, lastChild = lastNoneSpaceChild( block );
32                if ( lastChild )
33                {
34                        if ( ( fromSource || !CKEDITOR.env.ie ) && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br' )
35                                children.pop();
36                        if ( lastChild.type == CKEDITOR.NODE_TEXT && tailNbspRegex.test( lastChild.value ) )
37                                children.pop();
38                }
39        }
40
41        function blockNeedsExtension( block )
42        {
43                var lastChild = lastNoneSpaceChild( block );
44
45                return !lastChild
46                        || lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br'
47                        // Some of the controls in form needs extension too,
48                        // to move cursor at the end of the form. (#4791)
49                        || block.name == 'form' && lastChild.name == 'input';
50        }
51
52        function extendBlockForDisplay( block )
53        {
54                trimFillers( block, true );
55
56                if ( blockNeedsExtension( block ) )
57                {
58                        if ( CKEDITOR.env.ie )
59                                block.add( new CKEDITOR.htmlParser.text( '\xa0' ) );
60                        else
61                                block.add( new CKEDITOR.htmlParser.element( 'br', {} ) );
62                }
63        }
64
65        function extendBlockForOutput( block )
66        {
67                trimFillers( block );
68
69                if ( blockNeedsExtension( block ) )
70                        block.add( new CKEDITOR.htmlParser.text( '\xa0' ) );
71        }
72
73        var dtd = CKEDITOR.dtd;
74
75        // Find out the list of block-like tags that can contain <br>.
76        var blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent );
77        for ( var i in blockLikeTags )
78                if ( ! ( i in Object.prototype ) )
79                {
80                        if ( ! ( 'br' in dtd[i] ) )
81                                delete blockLikeTags[i];
82                }
83        // We just avoid filler in <pre> right now.
84        // TODO: Support filler for <pre>, line break is also occupy line height.
85        delete blockLikeTags.pre;
86        var defaultDataFilterRules =
87        {
88                attributeNames :
89                [
90                        // Event attributes (onXYZ) must not be directly set. They can become
91                        // active in the editing area (IE|WebKit).
92                        [ ( /^on/ ), '_cke_pa_on' ]
93                ]
94        };
95
96        var defaultDataBlockFilterRules = { elements : {} };
97
98        for ( i in blockLikeTags )
99                defaultDataBlockFilterRules.elements[ i ] = extendBlockForDisplay;
100
101        var defaultHtmlFilterRules =
102                {
103                        elementNames :
104                        [
105                                // Remove the "cke:" namespace prefix.
106                                [ ( /^cke:/ ), '' ],
107
108                                // Ignore <?xml:namespace> tags.
109                                [ ( /^\?xml:namespace$/ ), '' ]
110                        ],
111
112                        attributeNames :
113                        [
114                                // Attributes saved for changes and protected attributes.
115                                [ ( /^_cke_(saved|pa)_/ ), '' ],
116
117                                // All "_cke" attributes are to be ignored.
118                                [ ( /^_cke.*/ ), '' ],
119
120                                [ 'hidefocus', '' ]
121                        ],
122
123                        elements :
124                        {
125                                $ : function( element )
126                                {
127                                        var attribs = element.attributes;
128
129                                        if ( attribs )
130                                        {
131                                                // Elements marked as temporary are to be ignored.
132                                                if ( attribs.cke_temp )
133                                                        return false;
134
135                                                // Remove duplicated attributes - #3789.
136                                                var attributeNames = [ 'name', 'href', 'src' ],
137                                                        savedAttributeName;
138                                                for ( var i = 0 ; i < attributeNames.length ; i++ )
139                                                {
140                                                        savedAttributeName = '_cke_saved_' + attributeNames[ i ];
141                                                        savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] );
142                                                }
143                                        }
144
145                                        return element;
146                                },
147
148                                embed : function( element )
149                                {
150                                        var parent = element.parent;
151
152                                        // If the <embed> is child of a <object>, copy the width
153                                        // and height attributes from it.
154                                        if ( parent && parent.name == 'object' )
155                                        {
156                                                var parentWidth = parent.attributes.width,
157                                                        parentHeight = parent.attributes.height;
158                                                parentWidth && ( element.attributes.width = parentWidth );
159                                                parentHeight && ( element.attributes.height = parentHeight );
160                                        }
161                                },
162                                // Restore param elements into self-closing.
163                                param : function( param )
164                                {
165                                        param.children = [];
166                                        param.isEmpty = true;
167                                        return param;
168                                },
169
170                                // Remove empty link but not empty anchor.(#3829)
171                                a : function( element )
172                                {
173                                        if ( !( element.children.length ||
174                                                        element.attributes.name ||
175                                                        element.attributes._cke_saved_name ) )
176                                        {
177                                                return false;
178                                        }
179                                },
180
181                                body : function( element )
182                                {
183                                        delete element.attributes.spellcheck;
184                                        delete element.attributes.contenteditable;
185                                },
186
187                                style : function( element )
188                                {
189                                        var child = element.children[ 0 ];
190                                        child && child.value && ( child.value = CKEDITOR.tools.trim( child.value ));
191
192                                        if ( !element.attributes.type )
193                                                element.attributes.type = 'text/css';
194                                },
195
196                                title : function( element )
197                                {
198                                        element.children[ 0 ].value = element.attributes[ '_cke_title' ];
199                                }
200                        },
201
202                        attributes :
203                        {
204                                'class' : function( value, element )
205                                {
206                                        // Remove all class names starting with "cke_".
207                                        return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false;
208                                }
209                        },
210
211                        comment : function( contents )
212                        {
213                                // If this is a comment for protected source.
214                                if ( contents.substr( 0, protectedSourceMarker.length ) == protectedSourceMarker )
215                                {
216                                        // Remove the extra marker for real comments from it.
217                                        if ( contents.substr( protectedSourceMarker.length, 3 ) == '{C}' )
218                                                contents = contents.substr( protectedSourceMarker.length + 3 );
219                                        else
220                                                contents = contents.substr( protectedSourceMarker.length );
221
222                                        return new CKEDITOR.htmlParser.cdata( decodeURIComponent( contents ) );
223                                }
224
225                                return contents;
226                        }
227                };
228
229        var defaultHtmlBlockFilterRules = { elements : {} };
230
231        for ( i in blockLikeTags )
232                defaultHtmlBlockFilterRules.elements[ i ] = extendBlockForOutput;
233
234        if ( CKEDITOR.env.ie )
235        {
236                // IE outputs style attribute in capital letters. We should convert
237                // them back to lower case.
238                defaultHtmlFilterRules.attributes.style = function( value, element )
239                {
240                        return value.toLowerCase();
241                };
242        }
243
244        var protectAttributeRegex = /<(?:a|area|img|input)[\s\S]*?\s((?:href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))/gi;
245
246        var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,
247                encodedElementsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi;
248
249        var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,
250                unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi;
251
252        var protectSelfClosingRegex = /<cke:(param|embed)([^>]*?)\/?>(?!\s*<\/cke:\1)/gi;
253
254        function protectAttributes( html )
255        {
256                return html.replace( protectAttributeRegex, '$& _cke_saved_$1' );
257        }
258
259        function protectElements( html )
260        {
261                return html.replace( protectElementsRegex, function( match )
262                        {
263                                return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>';
264                        });
265        }
266
267        function unprotectElements( html )
268        {
269                return html.replace( encodedElementsRegex, function( match, encoded )
270                        {
271                                return decodeURIComponent( encoded );
272                        });
273        }
274
275        function protectElementsNames( html )
276        {
277                return html.replace( protectElementNamesRegex, '$1cke:$2');
278        }
279
280        function unprotectElementNames( html )
281        {
282                return html.replace( unprotectElementNamesRegex, '$1$2' );
283        }
284
285        function protectSelfClosingElements( html )
286        {
287                return html.replace( protectSelfClosingRegex, '<cke:$1$2></cke:$1>' );
288        }
289
290        function protectRealComments( html )
291        {
292                return html.replace( /<!--(?!{cke_protected})[\s\S]+?-->/g, function( match )
293                        {
294                                return '<!--' + protectedSourceMarker +
295                                                '{C}' +
296                                                encodeURIComponent( match ).replace( /--/g, '%2D%2D' ) +
297                                                '-->';
298                        });
299        }
300
301        function unprotectRealComments( html )
302        {
303                return html.replace( /<!--\{cke_protected\}\{C\}([\s\S]+?)-->/g, function( match, data )
304                        {
305                                return decodeURIComponent( data );
306                        });
307        }
308
309        function protectSource( data, protectRegexes )
310        {
311                var protectedHtml = [],
312                        tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g;
313
314                var regexes =
315                        [
316                                // Script tags will also be forced to be protected, otherwise
317                                // IE will execute them.
318                                ( /<script[\s\S]*?<\/script>/gi ),
319
320                                // <noscript> tags (get lost in IE and messed up in FF).
321                                /<noscript[\s\S]*?<\/noscript>/gi
322                        ]
323                        .concat( protectRegexes );
324
325                // First of any other protection, we must protect all comments
326                // to avoid loosing them (of course, IE related).
327                // Note that we use a different tag for comments, as we need to
328                // transform them when applying filters.
329                data = data.replace( (/<!--[\s\S]*?-->/g), function( match )
330                        {
331                                return  '<!--{cke_tempcomment}' + ( protectedHtml.push( match ) - 1 ) + '-->';
332                        });
333
334                for ( var i = 0 ; i < regexes.length ; i++ )
335                {
336                        data = data.replace( regexes[i], function( match )
337                                {
338                                        match = match.replace( tempRegex,               // There could be protected source inside another one. (#3869).
339                                                function( $, isComment, id )
340                                                {
341                                                        return protectedHtml[ id ];
342                                                }
343                                        );
344                                        return  '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->';
345                                });
346                }
347                data = data.replace( tempRegex, function( $, isComment, id )
348                        {
349                                return '<!--' + protectedSourceMarker +
350                                                ( isComment ? '{C}' : '' ) +
351                                                encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) +
352                                                '-->';
353                        }
354                );
355                return data;
356        }
357
358        CKEDITOR.plugins.add( 'htmldataprocessor',
359        {
360                requires : [ 'htmlwriter' ],
361
362                init : function( editor )
363                {
364                        var dataProcessor = editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor );
365
366                        dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
367
368                        dataProcessor.dataFilter.addRules( defaultDataFilterRules );
369                        dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
370                        dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
371                        dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
372                }
373        });
374
375        CKEDITOR.htmlDataProcessor = function( editor )
376        {
377                this.editor = editor;
378
379                this.writer = new CKEDITOR.htmlWriter();
380                this.dataFilter = new CKEDITOR.htmlParser.filter();
381                this.htmlFilter = new CKEDITOR.htmlParser.filter();
382        };
383
384        CKEDITOR.htmlDataProcessor.prototype =
385        {
386                toHtml : function( data, fixForBody )
387                {
388                        // The source data is already HTML, but we need to clean
389                        // it up and apply the filter.
390
391                        data = protectSource( data, this.editor.config.protectedSource );
392
393                        // Before anything, we must protect the URL attributes as the
394                        // browser may changing them when setting the innerHTML later in
395                        // the code.
396                        data = protectAttributes( data );
397
398                        // Protect elements than can't be set inside a DIV. E.g. IE removes
399                        // style tags from innerHTML. (#3710)
400                        data = protectElements( data );
401
402                        // Certain elements has problem to go through DOM operation, protect
403                        // them by prefixing 'cke' namespace. (#3591)
404                        data = protectElementsNames( data );
405
406                        // All none-IE browsers ignore self-closed custom elements,
407                        // protecting them into open-close. (#3591)
408                        data = protectSelfClosingElements( data );
409
410                        // Call the browser to help us fixing a possibly invalid HTML
411                        // structure.
412                        var div = new CKEDITOR.dom.element( 'div' );
413                        // Add fake character to workaround IE comments bug. (#3801)
414                        div.setHtml( 'a' + data );
415                        data = div.getHtml().substr( 1 );
416
417                        // Unprotect "some" of the protected elements at this point.
418                        data = unprotectElementNames( data );
419
420                        data = unprotectElements( data );
421
422                        // Restore the comments that have been protected, in this way they
423                        // can be properly filtered.
424                        data = unprotectRealComments( data );
425
426                        // Now use our parser to make further fixes to the structure, as
427                        // well as apply the filter.
428                        var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),
429                                writer = new CKEDITOR.htmlParser.basicWriter();
430
431                        fragment.writeHtml( writer, this.dataFilter );
432                        data = writer.getHtml( true );
433
434                        // Protect the real comments again.
435                        data = protectRealComments( data );
436
437                        return data;
438                },
439
440                toDataFormat : function( html, fixForBody )
441                {
442                        var writer = this.writer,
443                                fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody );
444
445                        writer.reset();
446
447                        fragment.writeHtml( writer, this.htmlFilter );
448
449                        return writer.getHtml( true );
450                }
451        };
452})();
453
454/**
455 * Whether to force using "&" instead of "&amp;amp;" in elements attributes
456 * values. It's not recommended to change this setting for compliance with the
457 * W3C XHTML 1.0 standards
458 * (<a href="http://www.w3.org/TR/xhtml1/#C_12">C.12, XHTML 1.0</a>).
459 * @type Boolean
460 * @default false
461 * @example
462 * config.forceSimpleAmpersand = false;
463 */
464CKEDITOR.config.forceSimpleAmpersand = false;
Note: See TracBrowser for help on using the repository browser.