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