source: trunk/phpgwapi/js/ckeditor/_source/plugins/pastefromword/filter/default.js @ 2862

Revision 2862, 36.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        var fragmentPrototype = CKEDITOR.htmlParser.fragment.prototype,
9                elementPrototype = CKEDITOR.htmlParser.element.prototype;
10
11        fragmentPrototype.onlyChild = elementPrototype.onlyChild = function()
12        {
13                var children = this.children,
14                        count = children.length,
15                        firstChild = ( count == 1 ) && children[ 0 ];
16                return firstChild || null;
17        };
18
19        elementPrototype.removeAnyChildWithName = function( tagName )
20        {
21                var children = this.children,
22                        childs = [],
23                        child;
24
25                for ( var i = 0; i < children.length; i++ )
26                {
27                        child = children[ i ];
28                        if ( !child.name )
29                                continue;
30
31                        if ( child.name == tagName )
32                        {
33                                childs.push( child );
34                                children.splice( i--, 1 );
35                        }
36                        childs = childs.concat( child.removeAnyChildWithName( tagName ) );
37                }
38                return childs;
39        };
40
41        elementPrototype.getAncestor = function( tagNameRegex )
42        {
43                var parent = this.parent;
44                while ( parent && !( parent.name && parent.name.match( tagNameRegex ) ) )
45                        parent = parent.parent;
46                return parent;
47        };
48
49        fragmentPrototype.firstChild = elementPrototype.firstChild = function( evaluator )
50        {
51                var child;
52
53                for ( var i = 0 ; i < this.children.length ; i++ )
54                {
55                        child = this.children[ i ];
56                        if ( evaluator( child ) )
57                                return child;
58                        else if ( child.name )
59                        {
60                                child = child.firstChild( evaluator );
61                                if ( child )
62                                        return child;
63                                else
64                                        continue;
65                        }
66                }
67
68                return null;
69        };
70
71        // Adding a (set) of styles to the element's attributes.
72        elementPrototype.addStyle = function( name, value, isPrepend )
73        {
74                var styleText, addingStyleText = '';
75                // name/value pair.
76                if ( typeof value == 'string' )
77                        addingStyleText += name + ':' + value + ';';
78                else
79                {
80                        // style literal.
81                        if ( typeof name == 'object' )
82                        {
83                                for ( var style in name )
84                                        if ( ! ( style in Object.prototype ) )
85                                        {
86                                                if ( name.hasOwnProperty( style ) )
87                                                        addingStyleText += style + ':' + name[ style ] + ';';
88                                        }
89                        }
90                        // raw style text form.
91                        else
92                                addingStyleText += name;
93
94                        isPrepend = value;
95                }
96
97                if ( !this.attributes )
98                        this.attributes = {};
99
100                styleText = this.attributes.style || '';
101
102                styleText = ( isPrepend ?
103                              [ addingStyleText, styleText ]
104                                          : [ styleText, addingStyleText ] ).join( ';' );
105
106                this.attributes.style = styleText.replace( /^;|;(?=;)/, '' );
107        };
108
109        /**
110         * Return the DTD-valid parent tag names of the specified one.
111         * @param tagName
112         */
113        CKEDITOR.dtd.parentOf = function( tagName )
114        {
115                var result = {};
116                for ( var tag in this )
117                {
118                        if ( tag.indexOf( '$' ) == -1 && this[ tag ][ tagName ] )
119                                result[ tag ] = 1;
120                }
121                return result;
122        };
123
124        var cssLengthRelativeUnit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz){1}?/i;
125        var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/;
126
127        var listBaseIndent = 0,
128                 previousListItemMargin;
129
130        CKEDITOR.plugins.pastefromword =
131        {
132                utils :
133                {
134                        // Create a <cke:listbullet> which indicate an list item type.
135                        createListBulletMarker : function ( bulletStyle, bulletText )
136                        {
137                                var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ),
138                                        listType;
139
140                                // TODO: Support more list style type from MS-Word.
141                                if ( !bulletStyle )
142                                {
143                                        bulletStyle = 'decimal';
144                                        listType = 'ol';
145                                }
146                                else if ( bulletStyle[ 2 ] )
147                                {
148                                        if ( !isNaN( bulletStyle[ 1 ] ) )
149                                                bulletStyle = 'decimal';
150                                        // No way to distinguish between Roman numerals and Alphas,
151                                        // detect them as a whole.
152                                        else if ( /^[a-z]+$/.test( bulletStyle[ 1 ] ) )
153                                                bulletStyle = 'lower-alpha';
154                                        else if ( /^[A-Z]+$/.test( bulletStyle[ 1 ] ) )
155                                                bulletStyle = 'upper-alpha';
156                                        // Simply use decimal for the rest forms of unrepresentable
157                                        // numerals, e.g. Chinese...
158                                        else
159                                                bulletStyle = 'decimal';
160
161                                        listType = 'ol';
162                                }
163                                else
164                                {
165                                        if ( /[l\u00B7\u2002]/.test( bulletStyle[ 1 ] ) ) //l·•
166                                                bulletStyle = 'disc';
167                                        else if ( /[\u006F\u00D8]/.test( bulletStyle[ 1 ] ) )  //oØ
168                                                bulletStyle = 'circle';
169                                        else if ( /[\u006E\u25C6]/.test( bulletStyle[ 1 ] ) ) //n◆
170                                                bulletStyle = 'square';
171                                        else
172                                                bulletStyle = 'disc';
173
174                                        listType = 'ul';
175                                }
176
177                                // Represent list type as CSS style.
178                                marker.attributes =
179                                {
180                                        'cke:listtype' : listType,
181                                        'style' : 'list-style-type:' + bulletStyle + ';'
182                                };
183                                marker.add( new CKEDITOR.htmlParser.text( bulletText ) );
184                                return marker;
185                        },
186
187                        isListBulletIndicator : function( element )
188                        {
189                                var styleText = element.attributes && element.attributes.style;
190                                if ( /mso-list\s*:\s*Ignore/i.test( styleText ) )
191                                        return true;
192                        },
193
194                        isContainingOnlySpaces : function( element )
195                        {
196                                var text;
197                                return ( ( text = element.onlyChild() )
198                                            && ( /^(:?\s|&nbsp;)+$/ ).test( text.value ) );
199                        },
200
201                        resolveList : function( element )
202                        {
203                                // <cke:listbullet> indicate a list item.
204                                var children = element.children,
205                                        attrs = element.attributes,
206                                        listMarker;
207
208                                if ( ( listMarker = element.removeAnyChildWithName( 'cke:listbullet' ) )
209                                          && listMarker.length
210                                          && ( listMarker = listMarker[ 0 ] ) )
211                                {
212                                        element.name = 'cke:li';
213
214                                        if ( attrs.style )
215                                        {
216                                                attrs.style = CKEDITOR.plugins.pastefromword.filters.stylesFilter(
217                                                                [
218                                                                        // Text-indent is not representing list item level any more.
219                                                                        [ 'text-indent' ],
220                                                                        [ 'line-height' ],
221                                                                        // Resolve indent level from 'margin-left' value.
222                                                                        [ ( /^margin(:?-left)?$/ ), null, function( margin )
223                                                                        {
224                                                                                // Be able to deal with component/short-hand form style.
225                                                                                var values = margin.split( ' ' );
226                                                                                margin = values[ 3 ] || values[ 1 ] || values [ 0 ];
227                                                                                margin = parseInt( margin, 10 );
228
229                                                                                // Figure out the indent unit by looking at the first increament.
230                                                                                if ( !listBaseIndent && previousListItemMargin && margin > previousListItemMargin )
231                                                                                        listBaseIndent = margin - previousListItemMargin;
232
233                                                                                attrs[ 'cke:margin' ] = previousListItemMargin = margin;
234                                                                        } ]
235                                                        ] )( attrs.style, element ) || '' ;
236                                        }
237
238                                        // Inherit list-type-style from bullet.
239                                        var listBulletAttrs = listMarker.attributes,
240                                                listBulletStyle = listBulletAttrs.style;
241
242                                        element.addStyle( listBulletStyle );
243                                        CKEDITOR.tools.extend( attrs, listBulletAttrs );
244                                        return true;
245                                }
246
247                                return false;
248                        },
249
250                        // Convert various length units to 'px' in ignorance of DPI.
251                        convertToPx : ( function ()
252                        {
253                                var calculator = CKEDITOR.dom.element.createFromHtml(
254                                                                '<div style="position:absolute;left:-9999px;' +
255                                                                'top:-9999px;margin:0px;padding:0px;border:0px;"' +
256                                                                '></div>', CKEDITOR.document );
257                                CKEDITOR.document.getBody().append( calculator );
258
259                                return function( cssLength )
260                                {
261                                        if ( cssLengthRelativeUnit.test( cssLength ) )
262                                        {
263                                                calculator.setStyle( 'width', cssLength );
264                                                return calculator.$.clientWidth + 'px';
265                                        }
266
267                                        return cssLength;
268                                };
269                        } )(),
270
271                        // Providing a shorthand style then retrieve one or more style component values.
272                        getStyleComponents : ( function()
273                        {
274                                var calculator = CKEDITOR.dom.element.createFromHtml(
275                                                                '<div style="position:absolute;left:-9999px;top:-9999px;"></div>',
276                                                                CKEDITOR.document );
277                                CKEDITOR.document.getBody().append( calculator );
278
279                                return function( name, styleValue, fetchList )
280                                {
281                                        calculator.setStyle( name, styleValue );
282                                        var styles = {},
283                                                count = fetchList.length;
284                                        for ( var i = 0; i < count; i++ )
285                                                styles[ fetchList[ i ] ]  = calculator.getStyle( fetchList[ i ] );
286
287                                        return styles;
288                                };
289                        } )(),
290
291                        listDtdParents : CKEDITOR.dtd.parentOf( 'ol' )
292                },
293
294                filters :
295                {
296                                // Transform a normal list into flat list items only presentation.
297                                // E.g. <ul><li>level1<ol><li>level2</li></ol></li> =>
298                                // <cke:li cke:listtype="ul" cke:indent="1">level1</cke:li>
299                                // <cke:li cke:listtype="ol" cke:indent="2">level2</cke:li>
300                                flattenList : function( element )
301                                {
302                                        var     attrs = element.attributes,
303                                                parent = element.parent;
304
305                                        var listStyleType,
306                                                indentLevel = 1;
307
308                                        // Resolve how many level nested.
309                                        while ( parent )
310                                        {
311                                                parent.attributes && parent.attributes[ 'cke:list'] && indentLevel++;
312                                                parent = parent.parent;
313                                        }
314
315                                        // All list items are of the same type.
316                                        switch ( attrs.type )
317                                        {
318                                                case 'a' :
319                                                        listStyleType = 'lower-alpha';
320                                                        break;
321                                                // TODO: Support more list style type from MS-Word.
322                                        }
323
324                                        var children = element.children,
325                                                child;
326
327                                        for ( var i = 0; i < children.length; i++ )
328                                        {
329                                                child = children[ i ];
330                                                var attributes = child.attributes;
331
332                                                if ( child.name in CKEDITOR.dtd.$listItem )
333                                                {
334                                                        var listItemChildren = child.children,
335                                                                count = listItemChildren.length,
336                                                                last = listItemChildren[ count - 1 ];
337
338                                                        // Move out nested list.
339                                                        if ( last.name in CKEDITOR.dtd.$list )
340                                                        {
341                                                                children.splice( i + 1, 0, last );
342                                                                last.parent = element;
343
344                                                                // Remove the parent list item if it's just a holder.
345                                                                if ( !--listItemChildren.length )
346                                                                        children.splice( i, 1 );
347                                                        }
348
349                                                        child.name = 'cke:li';
350                                                        attributes[ 'cke:indent' ] = indentLevel;
351                                                        previousListItemMargin = 0;
352                                                        attributes[ 'cke:listtype' ] = element.name;
353                                                        listStyleType && child.addStyle( 'list-style-type', listStyleType, true );
354                                                }
355                                        }
356
357                                        delete element.name;
358
359                                        // We're loosing tag name here, signalize this element as a list.
360                                        attrs[ 'cke:list' ] = 1;
361                                },
362
363                                /**
364                                 *  Try to collect all list items among the children and establish one
365                                 *  or more HTML list structures for them.
366                                 * @param element
367                                 */
368                                assembleList : function( element )
369                                {
370                                        var children = element.children, child,
371                                                        listItem,   // The current processing cke:li element.
372                                                        listItemAttrs,
373                                                        listType,   // Determine the root type of the list.
374                                                        listItemIndent, // Indent level of current list item.
375                                                        lastListItem, // The previous one just been added to the list.
376                                                        list, parentList, // Current staging list and it's parent list if any.
377                                                        indent;
378
379                                        for ( var i = 0; i < children.length; i++ )
380                                        {
381                                                child = children[ i ];
382
383                                                if ( 'cke:li' == child.name )
384                                                {
385                                                        child.name = 'li';
386                                                        listItem = child;
387                                                        listItemAttrs = listItem.attributes;
388                                                        listType = listItem.attributes[ 'cke:listtype' ];
389
390                                                        // List item indent level might come from a real list indentation or
391                                                        // been resolved from a pseudo list item's margin value, even get
392                                                        // no indentation at all.
393                                                        listItemIndent = parseInt( listItemAttrs[ 'cke:indent' ], 10 )
394                                                                                                        || listBaseIndent && ( Math.ceil( listItemAttrs[ 'cke:margin' ] / listBaseIndent ) )
395                                                                                                        || 1;
396
397                                                        // Ignore the 'list-style-type' attribute if it's matched with
398                                                        // the list root element's default style type.
399                                                        listItemAttrs.style && ( listItemAttrs.style =
400                                                                CKEDITOR.plugins.pastefromword.filters.stylesFilter(
401                                                                        [
402                                                                                [ 'list-style-type', listType == 'ol' ? 'decimal' : 'disc' ]
403                                                                        ] )( listItemAttrs.style )
404                                                                        || '' );
405
406                                                        if ( !list )
407                                                        {
408                                                                list = new CKEDITOR.htmlParser.element( listType );
409                                                                list.add( listItem );
410                                                                children[ i ] = list;
411                                                        }
412                                                        else
413                                                        {
414                                                                if ( listItemIndent > indent )
415                                                                {
416                                                                        list = new CKEDITOR.htmlParser.element( listType );
417                                                                        list.add( listItem );
418                                                                        lastListItem.add( list );
419                                                                }
420                                                                else if ( listItemIndent < indent )
421                                                                {
422                                                                        // There might be a negative gap between two list levels. (#4944)
423                                                                        var diff = indent - listItemIndent,
424                                                                                parent;
425                                                                        while ( diff-- && ( parent = list.parent ) )
426                                                                                list = parent.parent;
427
428                                                                        list.add( listItem );
429                                                                }
430                                                                else
431                                                                        list.add( listItem );
432
433                                                                children.splice( i--, 1 );
434                                                        }
435
436                                                        lastListItem = listItem;
437                                                        indent = listItemIndent;
438                                                }
439                                                else
440                                                        list = null;
441                                        }
442
443                                        listBaseIndent = 0;
444                                },
445
446                                /**
447                                 * A simple filter which always rejecting.
448                                 */
449                                falsyFilter : function( value )
450                                {
451                                        return false;
452                                },
453
454                                /**
455                                 * A filter dedicated on the 'style' attribute filtering, e.g. dropping/replacing style properties.
456                                 * @param styles {Array} in form of [ styleNameRegexp, styleValueRegexp,
457                                 *  newStyleValue/newStyleGenerator, newStyleName ] where only the first
458                                 *  parameter is mandatory.
459                                 * @param whitelist {Boolean} Whether the {@param styles} will be considered as a white-list.
460                                 */
461                                stylesFilter : function( styles, whitelist )
462                                {
463                                        return function( styleText, element )
464                                        {
465                                                 var rules = [];
466                                                // html-encoded quote might be introduced by 'font-family'
467                                                // from MS-Word which confused the following regexp. e.g.
468                                                //'font-family: &quot;Lucida, Console&quot;'
469                                                 styleText
470                                                        .replace( /&quot;/g, '"' )
471                                                        .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,
472                                                                 function( match, name, value )
473                                                                 {
474                                                                         name = name.toLowerCase();
475                                                                         name == 'font-family' && ( value = value.replace( /["']/g, '' ) );
476
477                                                                         var namePattern,
478                                                                                 valuePattern,
479                                                                                 newValue,
480                                                                                 newName;
481                                                                         for ( var i = 0 ; i < styles.length; i++ )
482                                                                         {
483                                                                                if ( styles[ i ] )
484                                                                                {
485                                                                                        namePattern = styles[ i ][ 0 ];
486                                                                                        valuePattern = styles[ i ][ 1 ];
487                                                                                        newValue = styles[ i ][ 2 ];
488                                                                                        newName = styles[ i ][ 3 ];
489
490                                                                                        if ( name.match( namePattern )
491                                                                                                 && ( !valuePattern || value.match( valuePattern ) ) )
492                                                                                        {
493                                                                                                name = newName || name;
494                                                                                                whitelist && ( newValue = newValue || value );
495
496                                                                                                if ( typeof newValue == 'function' )
497                                                                                                        newValue = newValue( value, element, name );
498
499                                                                                                // Return an couple indicate both name and value
500                                                                                                // changed.
501                                                                                                if ( newValue && newValue.push )
502                                                                                                        name = newValue[ 0 ], newValue = newValue[ 1 ];
503
504                                                                                                if ( typeof newValue == 'string' )
505                                                                                                        rules.push( [ name, newValue ] );
506                                                                                                return;
507                                                                                        }
508                                                                                }
509                                                                         }
510
511                                                                         !whitelist && rules.push( [ name, value ] );
512
513                                                                 });
514
515                                                for ( var i = 0 ; i < rules.length ; i++ )
516                                                         rules[ i ] = rules[ i ].join( ':' );
517                                                return rules.length ?
518                                                         ( rules.join( ';' ) + ';' ) : false;
519                                         };
520                                },
521
522                                /**
523                                 * Migrate the element by decorate styles on it.
524                                 * @param styleDefiniton
525                                 * @param variables
526                                 */
527                                elementMigrateFilter : function ( styleDefiniton, variables )
528                                {
529                                        return function( element )
530                                                {
531                                                        var styleDef =
532                                                                        variables ?
533                                                                                new CKEDITOR.style( styleDefiniton, variables )._.definition
534                                                                                : styleDefiniton;
535                                                        element.name = styleDef.element;
536                                                        CKEDITOR.tools.extend( element.attributes, CKEDITOR.tools.clone( styleDef.attributes ) );
537                                                        element.addStyle( CKEDITOR.style.getStyleText( styleDef ) );
538                                                };
539                                },
540
541                                /**
542                                 * Migrate styles by creating a new nested stylish element.
543                                 * @param styleDefinition
544                                 */
545                                styleMigrateFilter : function( styleDefinition, variableName )
546                                {
547
548                                        var elementMigrateFilter = this.elementMigrateFilter;
549                                        return function( value, element )
550                                        {
551                                                // Build an stylish element first.
552                                                var styleElement = new CKEDITOR.htmlParser.element( null ),
553                                                        variables = {};
554
555                                                variables[ variableName ] = value;
556                                                elementMigrateFilter( styleDefinition, variables )( styleElement );
557                                                // Place the new element inside the existing span.
558                                                styleElement.children = element.children;
559                                                element.children = [ styleElement ];
560                                        };
561                                },
562
563                                /**
564                                 * A filter which remove cke-namespaced-attribute on
565                                 * all none-cke-namespaced elements.
566                                 * @param value
567                                 * @param element
568                                 */
569                                bogusAttrFilter : function( value, element )
570                                {
571                                        if ( element.name.indexOf( 'cke:' ) == -1 )
572                                                return false;
573                                },
574
575                                /**
576                                 * A filter which will be used to apply inline css style according the stylesheet
577                                 * definition rules, is generated lazily when filtering.
578                                 */
579                                applyStyleFilter : null
580
581                        },
582
583                getRules : function( editor )
584                {
585                        var dtd = CKEDITOR.dtd,
586                                blockLike = CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ),
587                                config = editor.config,
588                                filters = this.filters,
589                                falsyFilter = filters.falsyFilter,
590                                stylesFilter = filters.stylesFilter,
591                                elementMigrateFilter = filters.elementMigrateFilter,
592                                styleMigrateFilter = CKEDITOR.tools.bind( this.filters.styleMigrateFilter, this.filters ),
593                                bogusAttrFilter = filters.bogusAttrFilter,
594                                createListBulletMarker = this.utils.createListBulletMarker,
595                                flattenList = filters.flattenList,
596                                assembleList = filters.assembleList,
597                                isListBulletIndicator = this.utils.isListBulletIndicator,
598                                containsNothingButSpaces = this.utils.isContainingOnlySpaces,
599                                resolveListItem = this.utils.resolveList,
600                                convertToPx = this.utils.convertToPx,
601                                getStyleComponents = this.utils.getStyleComponents,
602                                listDtdParents = this.utils.listDtdParents,
603                                removeFontStyles = config.pasteFromWordRemoveFontStyles !== false,
604                                removeStyles = config.pasteFromWordRemoveStyles !== false;
605
606                        return {
607
608                                elementNames :
609                                [
610                                        // Remove script, meta and link elements.
611                                        [ ( /meta|link|script/ ), '' ]
612                                ],
613
614                                root : function( element )
615                                {
616                                        element.filterChildren();
617                                        assembleList( element );
618                                },
619
620                                elements :
621                                {
622                                        '^' : function( element )
623                                        {
624                                                // Transform CSS style declaration to inline style.
625                                                var applyStyleFilter;
626                                                if ( CKEDITOR.env.gecko && ( applyStyleFilter = filters.applyStyleFilter ) )
627                                                        applyStyleFilter( element );
628                                        },
629
630                                        $ : function( element )
631                                        {
632                                                var tagName = element.name || '',
633                                                        attrs = element.attributes;
634
635                                                // Convert length unit of width/height on blocks to
636                                                // a more editor-friendly way (px).
637                                                if ( tagName in blockLike
638                                                        && attrs.style )
639                                                {
640                                                        attrs.style = stylesFilter(
641                                                                                [ [ ( /^(:?width|height)$/ ), null, convertToPx ] ] )( attrs.style ) || '';
642                                                }
643
644                                                // Processing headings.
645                                                if ( tagName.match( /h\d/ ) )
646                                                {
647                                                        element.filterChildren();
648                                                        // Is the heading actually a list item?
649                                                        if ( resolveListItem( element ) )
650                                                                return;
651
652                                                        // Adapt heading styles to editor's convention.
653                                                        elementMigrateFilter( config[ 'format_' + tagName ] )( element );
654                                                }
655                                                // Remove inline elements which contain only empty spaces.
656                                                else if ( tagName in dtd.$inline )
657                                                {
658                                                        element.filterChildren();
659                                                        if ( containsNothingButSpaces( element ) )
660                                                                delete element.name;
661                                                }
662                                                // Remove element with ms-office namespace,
663                                                // with it's content preserved, e.g. 'o:p'.
664                                                else if ( tagName.indexOf( ':' ) != -1
665                                                                 && tagName.indexOf( 'cke' ) == -1 )
666                                                {
667                                                        element.filterChildren();
668
669                                                        // Restore image real link from vml.
670                                                        if ( tagName == 'v:imagedata' )
671                                                        {
672                                                                var href = element.attributes[ 'o:href' ];
673                                                                if ( href )
674                                                                        element.attributes.src = href;
675                                                                element.name = 'img';
676                                                                return;
677                                                        }
678                                                        delete element.name;
679                                                }
680
681                                                // Assembling list items into a whole list.
682                                                if ( tagName in listDtdParents )
683                                                {
684                                                        element.filterChildren();
685                                                        assembleList( element );
686                                                }
687                                        },
688
689                                        // We'll drop any style sheet, but Firefox conclude
690                                        // certain styles in a single style element, which are
691                                        // required to be changed into inline ones.
692                                        'style' : function( element )
693                                        {
694                                                if ( CKEDITOR.env.gecko )
695                                                {
696                                                        // Grab only the style definition section.
697                                                        var styleDefSection = element.onlyChild().value.match( /\/\* Style Definitions \*\/([\s\S]*?)\/\*/ ),
698                                                                styleDefText = styleDefSection && styleDefSection[ 1 ],
699                                                                rules = {}; // Storing the parsed result.
700
701                                                        if ( styleDefText )
702                                                        {
703                                                                styleDefText
704                                                                        // Remove line-breaks.
705                                                                        .replace(/[\n\r]/g,'')
706                                                                        // Extract selectors and style properties.
707                                                                        .replace( /(.+?)\{(.+?)\}/g,
708                                                                                function( rule, selectors, styleBlock )
709                                                                                {
710                                                                                        selectors = selectors.split( ',' );
711                                                                                        var length = selectors.length, selector;
712                                                                                        for ( var i = 0; i < length; i++ )
713                                                                                        {
714                                                                                                // Assume MS-Word mostly generate only simple
715                                                                                                // selector( [Type selector][Class selector]).
716                                                                                                CKEDITOR.tools.trim( selectors[ i ] )
717                                                                                                                          .replace( /^(\w+)(\.[\w-]+)?$/g,
718                                                                                                function( match, tagName, className )
719                                                                                                {
720                                                                                                        tagName = tagName || '*';
721                                                                                                        className = className.substring( 1, className.length );
722
723                                                                                                        // Reject MS-Word Normal styles.
724                                                                                                        if ( className.match( /MsoNormal/ ) )
725                                                                                                                return;
726
727                                                                                                        if ( !rules[ tagName ] )
728                                                                                                                rules[ tagName ] = {};
729                                                                                                        if ( className )
730                                                                                                                rules[ tagName ][ className ] = styleBlock;
731                                                                                                        else
732                                                                                                                rules[ tagName ] = styleBlock;
733                                                                                                } );
734                                                                                        }
735                                                                                });
736
737                                                                filters.applyStyleFilter = function( element )
738                                                                {
739                                                                        var name = rules[ '*' ] ? '*' : element.name,
740                                                                                className = element.attributes && element.attributes[ 'class' ],
741                                                                                style;
742                                                                        if ( name in rules )
743                                                                        {
744                                                                                style = rules[ name ];
745                                                                                if ( typeof style == 'object' )
746                                                                                        style = style[ className ];
747                                                                                // Maintain style rules priorities.
748                                                                                style && element.addStyle( style, true );
749                                                                        }
750                                                                };
751                                                        }
752                                                }
753                                                return false;
754                                        },
755
756                                        'p' : function( element )
757                                        {
758                                                element.filterChildren();
759
760                                                var attrs = element.attributes,
761                                                        parent = element.parent,
762                                                        children = element.children;
763
764                                                // Is the paragraph actually a list item?
765                                                if ( resolveListItem( element ) )
766                                                        return;
767
768                                                // Adapt paragraph formatting to editor's convention
769                                                // according to enter-mode.
770                                                if ( config.enterMode == CKEDITOR.ENTER_BR )
771                                                {
772                                                        // We suffer from attribute/style lost in this situation.
773                                                        delete element.name;
774                                                        element.add( new CKEDITOR.htmlParser.element( 'br' ) );
775                                                }
776                                                else
777                                                        elementMigrateFilter( config[ 'format_' + ( config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ] )( element );
778                                        },
779
780                                        'div' : function( element )
781                                        {
782                                                // Aligned table with no text surrounded is represented by a wrapper div, from which
783                                                // table cells inherit as text-align styles, which is wrong.
784                                                // Instead we use a clear-float div after the table to properly achieve the same layout.
785                                                var singleChild = element.onlyChild();
786                                                if ( singleChild && singleChild.name == 'table' )
787                                                {
788                                                        var attrs = element.attributes;
789                                                        singleChild.attributes = CKEDITOR.tools.extend( singleChild.attributes, attrs );
790                                                        attrs.style && singleChild.addStyle( attrs.style );
791
792                                                        var clearFloatDiv = new CKEDITOR.htmlParser.element( 'div' );
793                                                        clearFloatDiv.addStyle( 'clear' ,'both' );
794                                                        element.add( clearFloatDiv );
795                                                        delete element.name;
796                                                }
797                                        },
798
799                                        'td' : function ( element )
800                                        {
801                                                // 'td' in 'thead' is actually <th>.
802                                                if ( element.getAncestor( 'thead') )
803                                                        element.name = 'th';
804                                        },
805
806                                        // MS-Word sometimes present list as a mixing of normal list
807                                        // and pseudo-list, normalize the previous ones into pseudo form.
808                                        'ol' : flattenList,
809                                        'ul' : flattenList,
810                                        'dl' : flattenList,
811
812                                        'font' : function( element )
813                                        {
814                                                // IE/Safari: drop the font tag if it comes from list bullet text.
815                                                if ( !CKEDITOR.env.gecko && isListBulletIndicator( element.parent ) )
816                                                {
817                                                        delete element.name;
818                                                        return;
819                                                }
820
821                                                element.filterChildren();
822
823                                                var attrs = element.attributes,
824                                                        styleText = attrs.style,
825                                                        parent = element.parent;
826
827                                                if ( 'font' == parent.name )     // Merge nested <font> tags.
828                                                {
829                                                        CKEDITOR.tools.extend( parent.attributes,
830                                                                        element.attributes );
831                                                        styleText && parent.addStyle( styleText );
832                                                        delete element.name;
833                                                        return;
834                                                }
835                                                // Convert the merged into a span with all attributes preserved.
836                                                else
837                                                {
838                                                        styleText = styleText || '';
839                                                        // IE's having those deprecated attributes, normalize them.
840                                                        if ( attrs.color )
841                                                        {
842                                                                attrs.color != '#000000' && ( styleText += 'color:' + attrs.color + ';' );
843                                                                delete attrs.color;
844                                                        }
845                                                        if ( attrs.face )
846                                                        {
847                                                                styleText += 'font-family:' + attrs.face + ';';
848                                                                delete attrs.face;
849                                                        }
850                                                        // TODO: Mapping size in ranges of xx-small,
851                                                        // x-small, small, medium, large, x-large, xx-large.
852                                                        if ( attrs.size )
853                                                        {
854                                                                styleText += 'font-size:' +
855                                                                             ( attrs.size > 3 ? 'large'
856                                                                                             : ( attrs.size < 3 ? 'small' : 'medium' ) ) + ';';
857                                                                delete attrs.size;
858                                                        }
859
860                                                        element.name = 'span';
861                                                        element.addStyle( styleText );
862                                                }
863                                        },
864
865                                        'span' : function( element )
866                                        {
867                                                // IE/Safari: remove the span if it comes from list bullet text.
868                                                if ( !CKEDITOR.env.gecko && isListBulletIndicator( element.parent ) )
869                                                        return false;
870
871                                                element.filterChildren();
872                                                if ( containsNothingButSpaces( element ) )
873                                                {
874                                                        delete element.name;
875                                                        return null;
876                                                }
877
878                                                // For IE/Safari: List item bullet type is supposed to be indicated by
879                                                // the text of a span with style 'mso-list : Ignore' or an image.
880                                                if ( !CKEDITOR.env.gecko && isListBulletIndicator( element ) )
881                                                {
882                                                        var listSymbolNode = element.firstChild( function( node )
883                                                        {
884                                                                return node.value || node.name == 'img';
885                                                        });
886
887                                                        var listSymbol =  listSymbolNode && ( listSymbolNode.value || 'l.' ),
888                                                                listType = listSymbol.match( /^([^\s]+?)([.)]?)$/ );
889                                                        return createListBulletMarker( listType, listSymbol );
890                                                }
891
892                                                // Update the src attribute of image element with href.
893                                                var children = element.children,
894                                                        attrs = element.attributes,
895                                                        styleText = attrs && attrs.style,
896                                                        firstChild = children && children[ 0 ];
897
898                                                // Assume MS-Word mostly carry font related styles on <span>,
899                                                // adapting them to editor's convention.
900                                                if ( styleText )
901                                                {
902                                                        attrs.style = stylesFilter(
903                                                                        [
904                                                                                // Drop 'inline-height' style which make lines overlapping.
905                                                                                [ 'line-height' ],
906                                                                                [ ( /^font-family$/ ), null, !removeFontStyles ? styleMigrateFilter( config[ 'font_style' ], 'family' ) : null ] ,
907                                                                                [ ( /^font-size$/ ), null, !removeFontStyles ? styleMigrateFilter( config[ 'fontSize_style' ], 'size' ) : null ] ,
908                                                                                [ ( /^color$/ ), null, !removeFontStyles ? styleMigrateFilter( config[ 'colorButton_foreStyle' ], 'color' ) : null ] ,
909                                                                                [ ( /^background-color$/ ), null, !removeFontStyles ? styleMigrateFilter( config[ 'colorButton_backStyle' ], 'color' ) : null ]
910                                                                        ] )( styleText, element ) || '';
911                                                }
912
913                                                return null;
914                                        },
915
916                                        // Migrate basic style formats to editor configured ones.
917                                        'b' : elementMigrateFilter( config[ 'coreStyles_bold' ] ),
918                                        'i' : elementMigrateFilter( config[ 'coreStyles_italic' ] ),
919                                        'u' : elementMigrateFilter( config[ 'coreStyles_underline' ] ),
920                                        's' : elementMigrateFilter( config[ 'coreStyles_strike' ] ),
921                                        'sup' : elementMigrateFilter( config[ 'coreStyles_superscript' ] ),
922                                        'sub' : elementMigrateFilter( config[ 'coreStyles_subscript' ] ),
923                                        // Editor doesn't support anchor with content currently (#3582),
924                                        // drop such anchors with content preserved.
925                                        'a' : function( element )
926                                        {
927                                                var attrs = element.attributes;
928                                                if ( attrs && !attrs.href && attrs.name )
929                                                        delete element.name;
930                                        },
931                                        'cke:listbullet' : function( element )
932                                        {
933                                                if ( element.getAncestor( /h\d/ ) && !config.pasteFromWordNumberedHeadingToList )
934                                                        delete element.name;
935                                                }
936                                },
937
938                                attributeNames :
939                                [
940                                        // Remove onmouseover and onmouseout events (from MS Word comments effect)
941                                        [ ( /^onmouse(:?out|over)/ ), '' ],
942                                        // Onload on image element.
943                                        [ ( /^onload$/ ), '' ],
944                                        // Remove office and vml attribute from elements.
945                                        [ ( /(?:v|o):\w+/ ), '' ],
946                                        // Remove lang/language attributes.
947                                        [ ( /^lang/ ), '' ]
948                                ],
949
950                                attributes :
951                                {
952                                        'style' : stylesFilter(
953                                        removeStyles ?
954                                        // Provide a white-list of styles that we preserve, those should
955                                        // be the ones that could later be altered with editor tools.
956                                        [
957                                                // Preserve margin-left/right which used as default indent style in the editor.
958                                                [ ( /^margin$|^margin-(?!bottom|top)/ ), null, function( value, element, name )
959                                                        {
960                                                                if ( element.name in { p : 1, div : 1 } )
961                                                                {
962                                                                        var indentStyleName = config.contentsLangDirection == 'ltr' ?
963                                                                                        'margin-left' : 'margin-right';
964
965                                                                        // Extract component value from 'margin' shorthand.
966                                                                        if ( name == 'margin' )
967                                                                        {
968                                                                                value = getStyleComponents( name, value,
969                                                                                                [ indentStyleName ] )[ indentStyleName ];
970                                                                        }
971                                                                        else if ( name != indentStyleName )
972                                                                                return null;
973
974                                                                        if ( value && !emptyMarginRegex.test( value ) )
975                                                                                return [ indentStyleName, value ];
976                                                                }
977
978                                                                return null;
979                                                        } ],
980
981                                                // Preserve clear float style.
982                                                [ ( /^clear$/ ) ],
983
984                                                [ ( /^border.*|margin.*|vertical-align|float$/ ), null,
985                                                        function( value, element )
986                                                        {
987                                                                if ( element.name == 'img' )
988                                                                        return value;
989                                                        } ],
990
991                                                [ (/^width|height$/ ), null,
992                                                        function( value, element )
993                                                        {
994                                                                if ( element.name in { table : 1, td : 1, th : 1, img : 1 } )
995                                                                        return value;
996                                                        } ]
997                                        ] :
998                                        // Otherwise provide a black-list of styles that we remove.
999                                        [
1000                                                [ ( /^mso-/ ) ],
1001                                                // Fixing color values.
1002                                                [ ( /-color$/ ), null, function( value )
1003                                                {
1004                                                        if ( value == 'transparent' )
1005                                                                return false;
1006                                                        if ( CKEDITOR.env.gecko )
1007                                                                return value.replace( /-moz-use-text-color/g, 'transparent' );
1008                                                } ],
1009                                                // Remove empty margin values, e.g. 0.00001pt 0em 0pt
1010                                                [ ( /^margin$/ ), emptyMarginRegex ],
1011                                                [ 'text-indent', '0cm' ],
1012                                                [ 'page-break-before' ],
1013                                                [ 'tab-stops' ],
1014                                                [ 'display', 'none' ],
1015                                                removeFontStyles ? [ ( /font-?/ ) ] : null
1016                                        ], removeStyles ),
1017
1018                                        // Prefer width styles over 'width' attributes.
1019                                        'width' : function( value, element )
1020                                        {
1021                                                if ( element.name in dtd.$tableContent )
1022                                                        return false;
1023                                        },
1024                                        // Prefer border styles over table 'border' attributes.
1025                                        'border' : function( value, element )
1026                                        {
1027                                                if ( element.name in dtd.$tableContent )
1028                                                        return false;
1029                                        },
1030
1031                                        // Only Firefox carry style sheet from MS-Word, which
1032                                        // will be applied by us manually. For other browsers
1033                                        // the css className is useless.
1034                                        'class' : falsyFilter,
1035
1036                                        // MS-Word always generate 'background-color' along with 'bgcolor',
1037                                        // simply drop the deprecated attributes.
1038                                        'bgcolor' : falsyFilter,
1039
1040                                        // Deprecate 'valign' attribute in favor of 'vertical-align'.
1041                                        'valign' : removeStyles ? falsyFilter : function( value, element )
1042                                        {
1043                                                element.addStyle( 'vertical-align', value );
1044                                                return false;
1045                                        }
1046                                },
1047
1048                                // Fore none-IE, some useful data might be buried under these IE-conditional
1049                                // comments where RegExp were the right approach to dig them out where usual approach
1050                                // is transform it into a fake element node which hold the desired data.
1051                                comment :
1052                                        !CKEDITOR.env.ie ?
1053                                                function( value, node )
1054                                                {
1055                                                        var imageInfo = value.match( /<img.*?>/ ),
1056                                                                listInfo = value.match( /^\[if !supportLists\]([\s\S]*?)\[endif\]$/ );
1057
1058                                                        // Seek for list bullet indicator.
1059                                                        if ( listInfo )
1060                                                        {
1061                                                                // Bullet symbol could be either text or an image.
1062                                                                var listSymbol = listInfo[ 1 ] || ( imageInfo && 'l.' ),
1063                                                                        listType = listSymbol && listSymbol.match( />([^\s]+?)([.)]?)</ );
1064                                                                return createListBulletMarker( listType, listSymbol );
1065                                                        }
1066
1067                                                        // Reveal the <img> element in conditional comments for Firefox.
1068                                                        if ( CKEDITOR.env.gecko && imageInfo )
1069                                                        {
1070                                                                var img = CKEDITOR.htmlParser.fragment.fromHtml( imageInfo[ 0 ] ).children[ 0 ],
1071                                                                        previousComment = node.previous,
1072                                                                        // Try to dig the real image link from vml markup from previous comment text.
1073                                                                        imgSrcInfo = previousComment && previousComment.value.match( /<v:imagedata[^>]*o:href=['"](.*?)['"]/ ),
1074                                                                        imgSrc = imgSrcInfo && imgSrcInfo[ 1 ];
1075
1076                                                                // Is there a real 'src' url to be used?
1077                                                                imgSrc && ( img.attributes.src = imgSrc );
1078                                                                return img;
1079                                                        }
1080
1081                                                        return false;
1082                                                }
1083                                        : falsyFilter
1084                        };
1085                }
1086        };
1087
1088        // The paste processor here is just a reduced copy of html data processor.
1089        var pasteProcessor = function()
1090        {
1091                this.dataFilter = new CKEDITOR.htmlParser.filter();
1092        };
1093
1094        pasteProcessor.prototype =
1095        {
1096                toHtml : function( data )
1097                {
1098                        var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, false ),
1099                                writer = new CKEDITOR.htmlParser.basicWriter();
1100
1101                        fragment.writeHtml( writer, this.dataFilter );
1102                        return writer.getHtml( true );
1103                }
1104        };
1105
1106        CKEDITOR.cleanWord = function( data, editor )
1107        {
1108                // Firefox will be confused by those downlevel-revealed IE conditional
1109                // comments, fixing them first( convert it to upperlevel-revealed one ).
1110                // e.g. <![if !vml]>...<![endif]>
1111                if ( CKEDITOR.env.gecko )
1112                        data = data.replace( /(<!--\[if[^<]*?\])-->([\S\s]*?)<!--(\[endif\]-->)/gi, '$1$2$3' );
1113
1114                var dataProcessor = new pasteProcessor(),
1115                        dataFilter = dataProcessor.dataFilter;
1116
1117                // These rules will have higher priorities than default ones.
1118                dataFilter.addRules( CKEDITOR.plugins.pastefromword.getRules( editor ) );
1119
1120                // Allow extending data filter rules.
1121                editor.fire( 'beforeCleanWord', { filter : dataFilter } );
1122
1123                try
1124                {
1125                        data = dataProcessor.toHtml( data, false );
1126                }
1127                catch ( e )
1128                {
1129                        alert( editor.lang.pastefromword.error );
1130                }
1131
1132                /* Below post processing those things that are unable to delivered by filter rules. */
1133
1134                // Remove 'cke' namespaced attribute used in filter rules as marker.
1135                data = data.replace( /cke:.*?".*?"/g, '' );
1136
1137                // Remove empty style attribute.
1138                data = data.replace( /style=""/g, '' );
1139
1140                // Remove the dummy spans ( having no inline style ).
1141                data = data.replace( /<span>/g, '' );
1142
1143                return data;
1144        };
1145})();
1146
1147/**
1148 * Whether to ignore all font related formatting styles, including:
1149 * <ul> <li>font size;</li>
1150 *              <li>font family;</li>
1151 *              <li>font foreground/background color.</li></ul>
1152 * @name CKEDITOR.config.pasteFromWordRemoveFontStyles
1153 * @since 3.1
1154 * @type Boolean
1155 * @default true
1156 * @example
1157 * config.pasteFromWordRemoveFontStyles = false;
1158 */
1159
1160/**
1161 * Whether to transform MS Word outline numbered headings into lists.
1162 * @name CKEDITOR.config.pasteFromWordNumberedHeadingToList
1163 * @since 3.1
1164 * @type Boolean
1165 * @default false
1166 * @example
1167 * config.pasteFromWordNumberedHeadingToList = true;
1168 */
1169
1170/**
1171 * Whether to remove element styles that can't be managed with the editor. Note
1172 * that this doesn't handle the font specific styles, which depends on the
1173 * {@link CKEDITOR.config.pasteFromWordRemoveFontStyles} setting instead.
1174 * @name CKEDITOR.config.pasteFromWordRemoveStyles
1175 * @since 3.1
1176 * @type Boolean
1177 * @default true
1178 * @example
1179 * config.pasteFromWordRemoveStyles = false;
1180 */
Note: See TracBrowser for help on using the repository browser.