| 121 | // 1. move list item styles up to list root if they're consistent. |
| 122 | // 2. clear out verbose 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 | if ( child.attributes.value && Number( child.attributes.value ) == i + 1 ) |
| 142 | delete child.attributes.value; |
| 143 | |
| 144 | match = styleTypeRegexp.exec( child.attributes.style ); |
| 145 | |
| 146 | if ( match ) |
| 147 | { |
| 148 | if ( match[ 1 ] == mergeStyle || !mergeStyle ) |
| 149 | mergeStyle = match[ 1 ]; |
| 150 | else |
| 151 | { |
| 152 | mergeStyle = null; |
| 153 | break; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | if ( mergeStyle ) |
| 159 | { |
| 160 | for ( i = 0; i < count; i++ ) |
| 161 | { |
| 162 | attrs = children[ i ].attributes; |
| 163 | attrs.style && ( attrs.style = stylesFilter( [ [ 'list-style-type'] ] )( attrs.style ) || '' ); |
| 164 | } |
| 165 | |
| 166 | list.addStyle( 'list-style-type', mergeStyle ); |
| 167 | } |
| 168 | } |
| 169 | |
121 | 170 | 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 | 171 | var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/; // e.g. 0px 0pt 0px |
123 | 172 | var romanLiternalPattern = '^m{0,4}(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$', |
124 | 173 | lowerRomanLiteralRegex = new RegExp( romanLiternalPattern ), |
125 | 174 | upperRomanLiteralRegex = new RegExp( romanLiternalPattern.toUpperCase() ); |
126 | 175 | |
127 | | var listBaseIndent = 0, |
128 | | previousListItemMargin; |
| 176 | var orderedPatterns = { 'decimal' : /\d+/, 'lower-roman': lowerRomanLiteralRegex, 'upper-roman': upperRomanLiteralRegex, 'lower-alpha' : /^[a-z]+$/, 'upper-alpha': /^[A-Z]+$/ }, |
| 177 | unorderedPatterns = { 'disc' : /[l\u00B7\u2002]/, 'circle' : /[\u006F\u00D8]/,'square' : /[\u006E\u25C6]/}, |
| 178 | listMarkerPatterns = { 'ol' : orderedPatterns, 'ul' : unorderedPatterns }, |
| 179 | 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'] ], |
| 180 | alpahbets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
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; |
| 182 | // Convert roman numbering back to decimal. |
| 183 | function fromRoman( str ) |
| 184 | { |
| 185 | str = str.toUpperCase(); |
| 186 | var l = romans.length, retVal = 0; |
| 187 | for ( var i = 0; i < l; ++i ) |
| 188 | { |
| 189 | for ( var j = romans[i], k = j[1].length; str.substr( 0, k ) == j[1]; str = str.substr( k ) ) |
| 190 | retVal += j[ 0 ]; |
| 191 | } |
| 192 | return retVal; |
| 193 | } |
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 | | } |
| 195 | // Convert alphabet numbering back to decimal. |
| 196 | function fromAlphabet( str ) |
| 197 | { |
| 198 | str = str.toUpperCase(); |
| 199 | var l = alpahbets.length, retVal = 1; |
| 200 | for ( var x = 1; str.length > 0; x *= l ) |
| 201 | { |
| 202 | retVal += alpahbets.indexOf( str.charAt( str.length - 1 ) ) * x; |
| 203 | str = str.substr( 0, str.length - 1 ); |
| 204 | } |
| 205 | return retVal; |
| 206 | } |
179 | | // Represent list type as CSS style. |
180 | | marker.attributes = |
181 | | { |
182 | | 'cke:listtype' : listType, |
183 | | 'style' : 'list-style-type:' + bulletStyle + ';' |
184 | | }; |
| 208 | var listBaseIndent = 0, |
| 209 | previousListItemMargin = null, |
| 210 | previousListId; |
| 211 | |
| 212 | CKEDITOR.plugins.pastefromword = |
| 213 | { |
| 214 | utils : |
| 215 | { |
| 216 | // Create a <cke:listbullet> which indicate an list item type. |
| 217 | createListBulletMarker : function ( bullet, bulletText ) |
| 218 | { |
| 219 | var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ); |
| 220 | marker.attributes = { 'cke:listsymbol' : bullet[ 0 ] }; |
234 | | attrs[ 'cke:margin' ] = previousListItemMargin = margin; |
| 270 | previousListItemMargin = margin; |
| 271 | |
| 272 | attrs[ 'cke:indent' ] = listBaseIndent && ( Math.ceil( margin / listBaseIndent ) + 1 ) || 1; |
| 273 | } ], |
| 274 | // The best situation: "mso-list:l0 level1 lfo2" tells the belonged list root, list item indentation, etc. |
| 275 | [ ( /^mso-list$/ ), null, function( val ) |
| 276 | { |
| 277 | val = val.split( ' ' ); |
| 278 | var listId = Number( val[ 0 ].match( /\d+/ ) ), |
| 279 | indent = Number( val[ 1 ].match( /\d+/ ) ); |
| 280 | |
| 281 | if ( listId != previousListId ) |
| 282 | attrs[ 'cke:reset' ] = 1; |
| 283 | |
| 284 | previousListId = listId; |
| 285 | attrs[ 'cke:indent' ] = indent; |
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 | | || '' ); |
| 478 | // We're moving out of the current list, cleaning up. |
| 479 | if ( listItemIndent != lastIndent ) |
| 480 | previousListType = previousListStyleType = null; |
| 482 | if ( !bullet ) |
| 483 | { |
| 484 | listType = 'ol'; |
| 485 | listStyleType = 'decimal'; |
| 486 | } |
| 487 | else |
| 488 | { |
| 489 | // Probably share the same list style type with previous list item, |
| 490 | // give it priority to avoid ambiguous between C(Alpha) and C.(Roman). |
| 491 | if ( previousListType && listMarkerPatterns[ previousListType ] [ previousListStyleType ].test( bullet[ 1 ] ) ) |
| 492 | { |
| 493 | listType = previousListType; |
| 494 | listStyleType = previousListStyleType; |
| 495 | } |
| 496 | else |
| 497 | { |
| 498 | for ( var type in listMarkerPatterns ) |
| 499 | { |
| 500 | for ( var style in listMarkerPatterns[ type ] ) |
| 501 | { |
| 502 | if ( listMarkerPatterns[ type ][ style ].test( bullet[ 1 ] ) ) |
| 503 | { |
| 504 | // Small numbering has higher priority, when dealing with ambiguous |
| 505 | // between C(Alpha) and C.(Roman). |
| 506 | if ( type == 'ol' && /alpha|roman/.test( style ) ) |
| 507 | { |
| 508 | var num = /roman/.test( style ) ? fromRoman( bullet[ 1 ] ) : fromAlphabet( bullet[ 1 ] ); |
| 509 | if ( !itemNumeric || num < itemNumeric ) |
| 510 | { |
| 511 | itemNumeric = num; |
| 512 | listType = type; |
| 513 | listStyleType = style; |
| 514 | } |
| 515 | } |
| 516 | else |
| 517 | { |
| 518 | listType = type; |
| 519 | listStyleType = style; |
| 520 | break; |
| 521 | } |
| 522 | } |
| 523 | } |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | // Simply use decimal/disc for the rest forms of unrepresentable |
| 528 | // numerals, e.g. Chinese..., but as long as there a second part |
| 529 | // included, it has a bigger chance of being a order list ;) |
| 530 | !listType && ( listType = bullet[ 2 ] ? 'ol' : 'ul' ); |
| 531 | } |
| 532 | |
| 533 | previousListType = listType; |
| 534 | previousListStyleType = listStyleType || ( listType == 'ol' ? 'decimal' : 'disc' ); |
| 535 | if ( listStyleType && listStyleType != ( listType == 'ol' ? 'decimal' : 'disc' ) ) |
| 536 | listItem.addStyle( 'list-style-type', listStyleType ); |
| 537 | |
| 538 | // Figure out start numbering. |
| 539 | if ( listType == 'ol' ) |
| 540 | { |
| 541 | switch ( listStyleType ) |
| 542 | { |
| 543 | case 'decimal' : |
| 544 | itemNumeric = Number( bullet[ 1 ] ); |
| 545 | break; |
| 546 | case 'lower-roman': |
| 547 | case 'upper-roman': |
| 548 | itemNumeric = fromRoman( bullet[ 1 ] ); |
| 549 | break; |
| 550 | case 'lower-alpha': |
| 551 | case 'upper-alpha': |
| 552 | itemNumeric = fromAlphabet( bullet[ 1 ] ); |
| 553 | break; |
| 554 | } |
| 555 | |
| 556 | // Always create the numbering, swipe out unnecessary ones later. |
| 557 | listItem.attributes.value = itemNumeric; |
| 558 | } |
| 559 | |
| 560 | // Start the list construction. |
897 | | listType = listSymbol.match( /^([^\s]+?)([.)]?)$/ ); |
898 | | return createListBulletMarker( listType, listSymbol ); |
| 1051 | listType = listSymbol.match( /^(?:[(]?)([^\s]+?)([.)]?)$/ ); |
| 1052 | |
| 1053 | var marker = createListBulletMarker( listType, listSymbol ); |
| 1054 | |
| 1055 | // Some non-existed list items might be carried by an inconsequential list, indicate by "mso-hide:all/display:none", |
| 1056 | // those are to be removed later, now mark it with "cke:ignored". |
| 1057 | var ancestor = element.getAncestor( 'span' ); |
| 1058 | if ( ancestor && /mso-hide:\s*all|display:\s*none/.test( ancestor.attributes.style ) ) |
| 1059 | marker.attributes[ 'cke:ignored' ] = 1; |
| 1060 | return marker; |