Changeset 6616 for CKEditor/trunk
- Timestamp:
- 03/28/11 05:31:22 (2 years ago)
- Location:
- CKEditor/trunk
- Files:
-
- 2 edited
-
CHANGES.html (modified) (1 diff)
-
_source/plugins/pastefromword/filter/default.js (modified) (17 diffs)
Legend:
- Unmodified
- Added
- Removed
-
CKEditor/trunk/CHANGES.html
r6614 r6616 44 44 <li><a href="http://dev.ckeditor.com/ticket/7315">#7315</a> : Fire the 'resize' event on dialog instances.</li> 45 45 <li><a href="http://dev.ckeditor.com/ticket/7259">#7259</a> : Dialog definition allows to specify initial 'width' and 'height' values.</li> 46 <li><a href="http://dev.ckeditor.com/ticket/7131">#7131</a> : List item numbering is now supported when paste from MS-Word.</li> 46 47 </ul> 47 48 <p> -
CKEditor/trunk/_source/plugins/pastefromword/filter/default.js
r6585 r6616 119 119 }; 120 120 121 // 1. move consistent list item styles up to list root. 122 // 2. clear out unnecessary list item numbering. 123 function postProcessList( list ) 124 { 125 var children = list.children, 126 child, 127 attrs, 128 count = list.children.length, 129 match, 130 mergeStyle, 131 styleTypeRegexp = /list-style-type:(.*?)(?:;|$)/, 132 stylesFilter = CKEDITOR.plugins.pastefromword.filters.stylesFilter; 133 134 attrs = list.attributes; 135 if ( styleTypeRegexp.exec( attrs.style ) ) 136 return; 137 138 for ( var i = 0; i < count; i++ ) 139 { 140 child = children[ i ]; 141 142 if ( child.attributes.value && Number( child.attributes.value ) == i + 1 ) 143 delete child.attributes.value; 144 145 match = styleTypeRegexp.exec( child.attributes.style ); 146 147 if ( match ) 148 { 149 if ( match[ 1 ] == mergeStyle || !mergeStyle ) 150 mergeStyle = match[ 1 ]; 151 else 152 { 153 mergeStyle = null; 154 break; 155 } 156 } 157 } 158 159 if ( mergeStyle ) 160 { 161 for ( i = 0; i < count; i++ ) 162 { 163 attrs = children[ i ].attributes; 164 attrs.style && ( attrs.style = stylesFilter( [ [ 'list-style-type'] ] )( attrs.style ) || '' ); 165 } 166 167 list.addStyle( 'list-style-type', mergeStyle ); 168 } 169 } 170 121 171 var cssLengthRelativeUnit = /^([.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz){1}?/i; 122 172 var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/; // e.g. 0px 0pt 0px … … 125 175 upperRomanLiteralRegex = new RegExp( romanLiternalPattern.toUpperCase() ); 126 176 177 var orderedPatterns = { 'decimal' : /\d+/, 'lower-roman': lowerRomanLiteralRegex, 'upper-roman': upperRomanLiteralRegex, 'lower-alpha' : /^[a-z]+$/, 'upper-alpha': /^[A-Z]+$/ }, 178 unorderedPatterns = { 'disc' : /[l\u00B7\u2002]/, 'circle' : /[\u006F\u00D8]/,'square' : /[\u006E\u25C6]/}, 179 listMarkerPatterns = { 'ol' : orderedPatterns, 'ul' : unorderedPatterns }, 180 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'] ], 181 alpahbets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 182 183 // Convert roman numbering back to decimal. 184 function fromRoman( str ) 185 { 186 str = str.toUpperCase(); 187 var l = romans.length, retVal = 0; 188 for ( var i = 0; i < l; ++i ) 189 { 190 for ( var j = romans[i], k = j[1].length; str.substr( 0, k ) == j[1]; str = str.substr( k ) ) 191 retVal += j[ 0 ]; 192 } 193 return retVal; 194 } 195 196 // Convert alphabet numbering back to decimal. 197 function fromAlphabet( str ) 198 { 199 str = str.toUpperCase(); 200 var l = alpahbets.length, retVal = 1; 201 for ( var x = 1; str.length > 0; x *= l ) 202 { 203 retVal += alpahbets.indexOf( str.charAt( str.length - 1 ) ) * x; 204 str = str.substr( 0, str.length - 1 ); 205 } 206 return retVal; 207 } 208 127 209 var listBaseIndent = 0, 128 previousListItemMargin; 129 130 CKEDITOR.plugins.pastefromword = 210 previousListItemMargin = null, 211 previousListId; 212 213 var plugin = ( CKEDITOR.plugins.pastefromword = 131 214 { 132 215 utils : 133 216 { 134 217 // Create a <cke:listbullet> which indicate an list item type. 135 createListBulletMarker : function ( bullet Style, bulletText )218 createListBulletMarker : function ( bullet, bulletText ) 136 219 { 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 else if ( lowerRomanLiteralRegex.test( bulletStyle[ 1 ] ) ) 151 bulletStyle = 'lower-roman'; 152 else if ( upperRomanLiteralRegex.test( bulletStyle[ 1 ] ) ) 153 bulletStyle = 'upper-roman'; 154 else if ( /^[a-z]+$/.test( bulletStyle[ 1 ] ) ) 155 bulletStyle = 'lower-alpha'; 156 else if ( /^[A-Z]+$/.test( bulletStyle[ 1 ] ) ) 157 bulletStyle = 'upper-alpha'; 158 // Simply use decimal for the rest forms of unrepresentable 159 // numerals, e.g. Chinese... 160 else 161 bulletStyle = 'decimal'; 162 163 listType = 'ol'; 164 } 165 else 166 { 167 if ( /[l\u00B7\u2002]/.test( bulletStyle[ 1 ] ) ) 168 bulletStyle = 'disc'; 169 else if ( /[\u006F\u00D8]/.test( bulletStyle[ 1 ] ) ) 170 bulletStyle = 'circle'; 171 else if ( /[\u006E\u25C6]/.test( bulletStyle[ 1 ] ) ) 172 bulletStyle = 'square'; 173 else 174 bulletStyle = 'disc'; 175 176 listType = 'ul'; 177 } 178 179 // Represent list type as CSS style. 180 marker.attributes = 181 { 182 'cke:listtype' : listType, 183 'style' : 'list-style-type:' + bulletStyle + ';' 184 }; 220 var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ); 221 marker.attributes = { 'cke:listsymbol' : bullet[ 0 ] }; 185 222 marker.add( new CKEDITOR.htmlParser.text( bulletText ) ); 186 223 return marker; … … 208 245 209 246 if ( ( listMarker = element.removeAnyChildWithName( 'cke:listbullet' ) ) 210 && listMarker.length211 && ( listMarker = listMarker[ 0 ] ) )247 && listMarker.length 248 && ( listMarker = listMarker[ 0 ] ) ) 212 249 { 213 250 element.name = 'cke:li'; … … 215 252 if ( attrs.style ) 216 253 { 217 attrs.style = CKEDITOR.plugins.pastefromword.filters.stylesFilter(254 attrs.style = plugin.filters.stylesFilter( 218 255 [ 219 256 // Text-indent is not representing list item level any more. 220 257 [ 'text-indent' ], 221 258 [ 'line-height' ], 222 // Resolve indent level from 'margin-left' value.259 // First attempt is to resolve indent level from on a constant margin increment. 223 260 [ ( /^margin(:?-left)?$/ ), null, function( margin ) 224 261 { 225 // Be able to deal with component/short-hand form style.262 // Deal with component/short-hand form. 226 263 var values = margin.split( ' ' ); 227 margin = CKEDITOR.plugins.pastefromword.utils.convertToPx( values[ 3 ] || values[ 1 ] || values [ 0 ] );264 margin = plugin.utils.convertToPx( values[ 3 ] || values[ 1 ] || values [ 0 ] ); 228 265 margin = parseInt( margin, 10 ); 229 266 230 // Figure out the indent unit by looking at the first increament.231 if ( !listBaseIndent && previousListItemMargin != undefined&& margin > previousListItemMargin )267 // Figure out the indent unit by checking the first time of incrementation. 268 if ( !listBaseIndent && previousListItemMargin != null && margin > previousListItemMargin ) 232 269 listBaseIndent = margin - previousListItemMargin; 233 270 234 attrs[ 'cke:margin' ] = previousListItemMargin = margin; 271 previousListItemMargin = margin; 272 273 attrs[ 'cke:indent' ] = listBaseIndent && ( Math.ceil( margin / listBaseIndent ) + 1 ) || 1; 274 } ], 275 // The best situation: "mso-list:l0 level1 lfo2" tells the belonged list root, list item indentation, etc. 276 [ ( /^mso-list$/ ), null, function( val ) 277 { 278 val = val.split( ' ' ); 279 var listId = Number( val[ 0 ].match( /\d+/ ) ), 280 indent = Number( val[ 1 ].match( /\d+/ ) ); 281 282 listId !== previousListId && ( attrs[ 'cke:reset' ] = 1 ); 283 previousListId = listId; 284 attrs[ 'cke:indent' ] = indent; 235 285 } ] 236 ] )( attrs.style, element ) || '';286 ] )( attrs.style, element ) || ''; 237 287 } 238 288 239 // First level list item are always presented without a margin. 240 !attrs[ 'cke:margin' ] && ( attrs[ 'cke:margin' ] = previousListItemMargin = 0 ); 241 242 // Inherit list-type-style from bullet. 243 var listBulletAttrs = listMarker.attributes, 244 listBulletStyle = listBulletAttrs.style; 245 246 element.addStyle( listBulletStyle ); 247 CKEDITOR.tools.extend( attrs, listBulletAttrs ); 289 // First level list item might be presented without a margin. 290 291 292 // In case all above doesn't apply. 293 if ( !attrs[ 'cke:indent' ] ) 294 { 295 previousListItemMargin = 0; 296 attrs[ 'cke:indent' ] = 1; 297 } 298 299 // Inherit attributes from bullet. 300 CKEDITOR.tools.extend( attrs, listMarker.attributes ); 248 301 return true; 249 302 } 303 // Current list disconnected. 304 else 305 previousListId = previousListItemMargin = listBaseIndent = null; 250 306 251 307 return false; … … 313 369 while ( parent ) 314 370 { 315 parent.attributes && parent.attributes[ 'cke:list' ] && indentLevel++;371 parent.attributes && parent.attributes[ 'cke:list' ] && indentLevel++; 316 372 parent = parent.parent; 317 373 } … … 352 408 353 409 child.name = 'cke:li'; 410 411 // Inherit numbering from list root on the first list item. 412 attrs.start && !i && ( attributes.value = attrs.start ); 413 414 plugin.filters.stylesFilter( 415 [ 416 [ 'tab-stops', null, function( val ) 417 { 418 var margin = val.split( ' ' )[ 1 ].match( cssLengthRelativeUnit ); 419 margin && ( previousListItemMargin = parseInt( plugin.utils.convertToPx( margin[ 0 ] ), 10 ) ); 420 } ], 421 [ 'mso-list', null, function( val ) 422 { 423 val = val.split( ' ' ); 424 var listId = Number( val[ 0 ].match( /\d+/ ) ); 425 listId !== previousListId && ( attributes[ 'cke:reset' ] = 1 ); 426 previousListId = listId; 427 } ] 428 ] )( attributes.style ); 429 354 430 attributes[ 'cke:indent' ] = indentLevel; 355 previousListItemMargin = 0;356 431 attributes[ 'cke:listtype' ] = element.name; 357 listStyleType && child.addStyle( 'list-style-type', listStyleType, true );432 attributes[ 'cke:list-style-type' ] = listStyleType; 358 433 } 359 434 } … … 375 450 listItem, // The current processing cke:li element. 376 451 listItemAttrs, 377 listType, // Determine the root type of the list.378 452 listItemIndent, // Indent level of current list item. 453 lastIndent, 379 454 lastListItem, // The previous one just been added to the list. 380 list, parentList, // Current staging list and it's parent list if any. 381 indent; 455 list, // Current staging list and it's parent list if any. 456 openedLists = [], 457 previousListStyleType, 458 previousListType; 459 460 // Properties of the list item are to be resolved from the list bullet. 461 var bullet, 462 listType, 463 listStyleType, 464 itemNumeric; 382 465 383 466 for ( var i = 0; i < children.length; i++ ) … … 390 473 listItem = child; 391 474 listItemAttrs = listItem.attributes; 392 listType = listItem.attributes[ 'cke:listtype' ]; 475 bullet = listItemAttrs[ 'cke:listsymbol' ]; 476 bullet = bullet && bullet.match( /^(?:[(]?)([^\s]+?)([.)]?)$/ ); 477 listType = listStyleType = itemNumeric = null; 478 479 if ( listItemAttrs[ 'cke:ignored' ] ) 480 { 481 children.splice( i--, 1 ); 482 continue; 483 } 484 485 486 // This's from a new list root. 487 listItemAttrs[ 'cke:reset' ] && ( list = lastIndent = lastListItem = null ); 393 488 394 489 // List item indent level might come from a real list indentation or 395 490 // been resolved from a pseudo list item's margin value, even get 396 491 // no indentation at all. 397 listItemIndent = parseInt( listItemAttrs[ 'cke:indent' ], 10 ) 398 || listBaseIndent && ( Math.ceil( listItemAttrs[ 'cke:margin' ] / listBaseIndent ) + 1 ) 399 || 1; 400 401 // Ignore the 'list-style-type' attribute if it's matched with 402 // the list root element's default style type. 403 listItemAttrs.style && ( listItemAttrs.style = 404 CKEDITOR.plugins.pastefromword.filters.stylesFilter( 405 [ 406 [ 'list-style-type', listType == 'ol' ? 'decimal' : 'disc' ] 407 ] )( listItemAttrs.style ) 408 || '' ); 409 492 listItemIndent = Number( listItemAttrs[ 'cke:indent' ] ); 493 494 // We're moving out of the current list, cleaning up. 495 if ( listItemIndent != lastIndent ) 496 previousListType = previousListStyleType = null; 497 498 // List type and item style are already resolved. 499 if ( !bullet ) 500 { 501 listType = listItemAttrs[ 'cke:listtype' ] || 'ol'; 502 listStyleType = listItemAttrs[ 'cke:list-style-type' ] || 'decimal'; 503 } 504 else 505 { 506 // Probably share the same list style type with previous list item, 507 // give it priority to avoid ambiguous between C(Alpha) and C.(Roman). 508 if ( previousListType && listMarkerPatterns[ previousListType ] [ previousListStyleType ].test( bullet[ 1 ] ) ) 509 { 510 listType = previousListType; 511 listStyleType = previousListStyleType; 512 } 513 else 514 { 515 for ( var type in listMarkerPatterns ) 516 { 517 for ( var style in listMarkerPatterns[ type ] ) 518 { 519 if ( listMarkerPatterns[ type ][ style ].test( bullet[ 1 ] ) ) 520 { 521 // Small numbering has higher priority, when dealing with ambiguous 522 // between C(Alpha) and C.(Roman). 523 if ( type == 'ol' && /alpha|roman/.test( style ) ) 524 { 525 var num = /roman/.test( style ) ? fromRoman( bullet[ 1 ] ) : fromAlphabet( bullet[ 1 ] ); 526 if ( !itemNumeric || num < itemNumeric ) 527 { 528 itemNumeric = num; 529 listType = type; 530 listStyleType = style; 531 } 532 } 533 else 534 { 535 listType = type; 536 listStyleType = style; 537 break; 538 } 539 } 540 } 541 } 542 } 543 544 // Simply use decimal/disc for the rest forms of unrepresentable 545 // numerals, e.g. Chinese..., but as long as there a second part 546 // included, it has a bigger chance of being a order list ;) 547 !listType && ( listType = bullet[ 2 ] ? 'ol' : 'ul' ); 548 } 549 550 previousListType = listType; 551 previousListStyleType = listStyleType || ( listType == 'ol' ? 'decimal' : 'disc' ); 552 if ( listStyleType && listStyleType != ( listType == 'ol' ? 'decimal' : 'disc' ) ) 553 listItem.addStyle( 'list-style-type', listStyleType ); 554 555 // Figure out start numbering. 556 if ( listType == 'ol' && bullet ) 557 { 558 switch ( listStyleType ) 559 { 560 case 'decimal' : 561 itemNumeric = Number( bullet[ 1 ] ); 562 break; 563 case 'lower-roman': 564 case 'upper-roman': 565 itemNumeric = fromRoman( bullet[ 1 ] ); 566 break; 567 case 'lower-alpha': 568 case 'upper-alpha': 569 itemNumeric = fromAlphabet( bullet[ 1 ] ); 570 break; 571 } 572 573 // Always create the numbering, swipe out unnecessary ones later. 574 listItem.attributes.value = itemNumeric; 575 } 576 577 // Start the list construction. 410 578 if ( !list ) 411 579 { 412 list = new CKEDITOR.htmlParser.element( listType);580 openedLists.push( list = new CKEDITOR.htmlParser.element( listType ) ); 413 581 list.add( listItem ); 414 582 children[ i ] = list; … … 416 584 else 417 585 { 418 if ( listItemIndent > indent )586 if ( listItemIndent > lastIndent ) 419 587 { 420 list = new CKEDITOR.htmlParser.element( listType);588 openedLists.push( list = new CKEDITOR.htmlParser.element( listType ) ); 421 589 list.add( listItem ); 422 590 lastListItem.add( list ); 423 591 } 424 else if ( listItemIndent < indent )592 else if ( listItemIndent < lastIndent ) 425 593 { 426 594 // There might be a negative gap between two list levels. (#4944) 427 var diff = indent - listItemIndent,428 parent;595 var diff = lastIndent - listItemIndent, 596 parent; 429 597 while ( diff-- && ( parent = list.parent ) ) 430 598 list = parent.parent; … … 439 607 440 608 lastListItem = listItem; 441 indent = listItemIndent;442 } 443 else 444 list = null;609 lastIndent = listItemIndent; 610 } 611 else if ( list ) 612 list = lastIndent = lastListItem = null; 445 613 } 446 614 447 listBaseIndent = 0; 615 for ( i = 0; i < openedLists.length; i++ ) 616 postProcessList( openedLists[ i ] ); 617 618 list = lastIndent = lastListItem = previousListId = previousListItemMargin = listBaseIndent = null; 448 619 }, 449 620 … … 471 642 // from MS-Word which confused the following regexp. e.g. 472 643 //'font-family: "Lucida, Console"' 473 styleText644 ( styleText || '' ) 474 645 .replace( /"/g, '"' ) 475 646 .replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, … … 764 935 if ( /MsoListParagraph/.exec( element.attributes[ 'class' ] ) ) 765 936 { 766 var bulletText = element.firstChild( function( node ) { return node.type == CKEDITOR.NODE_TEXT; } ); 937 var bulletText = element.firstChild( function( node ) 938 { 939 return node.type == CKEDITOR.NODE_TEXT && !containsNothingButSpaces( node.parent ); 940 }); 767 941 var bullet = bulletText && bulletText.parent, 768 942 bulletAttrs = bullet && bullet.attributes; … … 895 1069 896 1070 var listSymbol = listSymbolNode && ( listSymbolNode.value || 'l.' ), 897 listType = listSymbol.match( /^([^\s]+?)([.)]?)$/ ); 898 return createListBulletMarker( listType, listSymbol ); 1071 listType = listSymbol.match( /^(?:[(]?)([^\s]+?)([.)]?)$/ ); 1072 1073 if ( listType ) 1074 { 1075 var marker = createListBulletMarker( listType, listSymbol ); 1076 // Some non-existed list items might be carried by an inconsequential list, indicate by "mso-hide:all/display:none", 1077 // those are to be removed later, now mark it with "cke:ignored". 1078 var ancestor = element.getAncestor( 'span' ); 1079 if ( ancestor && /mso-hide:\s*all|display:\s*none/.test( ancestor.attributes.style ) ) 1080 marker.attributes[ 'cke:ignored' ] = 1; 1081 return marker; 1082 } 899 1083 } 900 1084 … … 942 1126 if ( element.getAncestor( /h\d/ ) && !config.pasteFromWordNumberedHeadingToList ) 943 1127 delete element.name; 944 }1128 } 945 1129 }, 946 1130 … … 964 1148 // be the ones that could later be altered with editor tools. 965 1149 [ 1150 // Leave list-style-type 1151 [ /^list-style-type$/, null ], 1152 966 1153 // Preserve margin-left/right which used as default indent style in the editor. 967 1154 [ ( /^margin$|^margin-(?!bottom|top)/ ), null, function( value, element, name ) … … 1070 1257 // Bullet symbol could be either text or an image. 1071 1258 var listSymbol = listInfo[ 1 ] || ( imageInfo && 'l.' ), 1072 listType = listSymbol && listSymbol.match( />( [^\s]+?)([.)]?)</ );1259 listType = listSymbol && listSymbol.match( />(?:[(]?)([^\s]+?)([.)]?)</ ); 1073 1260 return createListBulletMarker( listType, listSymbol ); 1074 1261 } … … 1093 1280 }; 1094 1281 } 1095 } ;1282 }); 1096 1283 1097 1284 // The paste processor here is just a reduced copy of html data processor.
Note: See TracChangeset
for help on using the changeset viewer.
