/* Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ (function() { var fragmentPrototype = CKEDITOR.htmlParser.fragment.prototype, elementPrototype = CKEDITOR.htmlParser.element.prototype; fragmentPrototype.onlyChild = elementPrototype.onlyChild = function() { var children = this.children, count = children.length, firstChild = ( count == 1 ) && children[ 0 ]; return firstChild || null; }; elementPrototype.removeAnyChildWithName = function( tagName ) { var children = this.children, childs = [], child; for ( var i = 0; i < children.length; i++ ) { child = children[ i ]; if ( !child.name ) continue; if ( child.name == tagName ) { childs.push( child ); children.splice( i--, 1 ); } childs = childs.concat( child.removeAnyChildWithName( tagName ) ); } return childs; }; elementPrototype.getAncestor = function( tagNameRegex ) { var parent = this.parent; while ( parent && !( parent.name && parent.name.match( tagNameRegex ) ) ) parent = parent.parent; return parent; }; fragmentPrototype.firstChild = elementPrototype.firstChild = function( evaluator ) { var child; for ( var i = 0 ; i < this.children.length ; i++ ) { child = this.children[ i ]; if ( evaluator( child ) ) return child; else if ( child.name ) { child = child.firstChild( evaluator ); if ( child ) return child; } } return null; }; // Adding a (set) of styles to the element's 'style' attributes. elementPrototype.addStyle = function( name, value, isPrepend ) { var styleText, addingStyleText = ''; // name/value pair. if ( typeof value == 'string' ) addingStyleText += name + ':' + value + ';'; else { // style literal. if ( typeof name == 'object' ) { for ( var style in name ) { if ( name.hasOwnProperty( style ) ) addingStyleText += style + ':' + name[ style ] + ';'; } } // raw style text form. else addingStyleText += name; isPrepend = value; } if ( !this.attributes ) this.attributes = {}; styleText = this.attributes.style || ''; styleText = ( isPrepend ? [ addingStyleText, styleText ] : [ styleText, addingStyleText ] ).join( ';' ); this.attributes.style = styleText.replace( /^;|;(?=;)/, '' ); }; /** * Return the DTD-valid parent tag names of the specified one. * @param tagName */ CKEDITOR.dtd.parentOf = function( tagName ) { var result = {}; for ( var tag in this ) { if ( tag.indexOf( '$' ) == -1 && this[ tag ][ tagName ] ) result[ tag ] = 1; } return result; }; // 1. move consistent list item styles up to list root. // 2. clear out unnecessary list item numbering. function postProcessList( list ) { var children = list.children, child, attrs, count = list.children.length, match, mergeStyle, styleTypeRegexp = /list-style-type:(.*?)(?:;|$)/, stylesFilter = CKEDITOR.plugins.pastefromword.filters.stylesFilter; attrs = list.attributes; if ( styleTypeRegexp.exec( attrs.style ) ) return; for ( var i = 0; i < count; i++ ) { child = children[ i ]; if ( child.attributes.value && Number( child.attributes.value ) == i + 1 ) delete child.attributes.value; match = styleTypeRegexp.exec( child.attributes.style ); if ( match ) { if ( match[ 1 ] == mergeStyle || !mergeStyle ) mergeStyle = match[ 1 ]; else { mergeStyle = null; break; } } } if ( mergeStyle ) { for ( i = 0; i < count; i++ ) { attrs = children[ i ].attributes; attrs.style && ( attrs.style = stylesFilter( [ [ 'list-style-type'] ] )( attrs.style ) || '' ); } list.addStyle( 'list-style-type', mergeStyle ); } } 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; var emptyMarginRegex = /^(?:\b0[^\s]*\s*){1,4}$/; // e.g. 0px 0pt 0px var romanLiternalPattern = '^m{0,4}(cm|cd|d?c{0,3})(xc|xl|l?x{0,3})(ix|iv|v?i{0,3})$', lowerRomanLiteralRegex = new RegExp( romanLiternalPattern ), upperRomanLiteralRegex = new RegExp( romanLiternalPattern.toUpperCase() ); var orderedPatterns = { 'decimal' : /\d+/, 'lower-roman': lowerRomanLiteralRegex, 'upper-roman': upperRomanLiteralRegex, 'lower-alpha' : /^[a-z]+$/, 'upper-alpha': /^[A-Z]+$/ }, unorderedPatterns = { 'disc' : /[l\u00B7\u2002]/, 'circle' : /[\u006F\u00D8]/,'square' : /[\u006E\u25C6]/}, listMarkerPatterns = { 'ol' : orderedPatterns, 'ul' : unorderedPatterns }, 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'] ], alpahbets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Convert roman numbering back to decimal. function fromRoman( str ) { str = str.toUpperCase(); var l = romans.length, retVal = 0; for ( var i = 0; i < l; ++i ) { for ( var j = romans[i], k = j[1].length; str.substr( 0, k ) == j[1]; str = str.substr( k ) ) retVal += j[ 0 ]; } return retVal; } // Convert alphabet numbering back to decimal. function fromAlphabet( str ) { str = str.toUpperCase(); var l = alpahbets.length, retVal = 1; for ( var x = 1; str.length > 0; x *= l ) { retVal += alpahbets.indexOf( str.charAt( str.length - 1 ) ) * x; str = str.substr( 0, str.length - 1 ); } return retVal; } var listBaseIndent = 0, previousListItemMargin = null, previousListId; var plugin = ( CKEDITOR.plugins.pastefromword = { utils : { // Create a which indicate an list item type. createListBulletMarker : function ( bullet, bulletText ) { var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ); marker.attributes = { 'cke:listsymbol' : bullet[ 0 ] }; marker.add( new CKEDITOR.htmlParser.text( bulletText ) ); return marker; }, isListBulletIndicator : function( element ) { var styleText = element.attributes && element.attributes.style; if ( /mso-list\s*:\s*Ignore/i.test( styleText ) ) return true; }, isContainingOnlySpaces : function( element ) { var text; return ( ( text = element.onlyChild() ) && ( /^(:?\s| )+$/ ).test( text.value ) ); }, resolveList : function( element ) { // indicate a list item. var attrs = element.attributes, listMarker; if ( ( listMarker = element.removeAnyChildWithName( 'cke:listbullet' ) ) && listMarker.length && ( listMarker = listMarker[ 0 ] ) ) { element.name = 'cke:li'; if ( attrs.style ) { attrs.style = plugin.filters.stylesFilter( [ // Text-indent is not representing list item level any more. [ 'text-indent' ], [ 'line-height' ], // First attempt is to resolve indent level from on a constant margin increment. [ ( /^margin(:?-left)?$/ ), null, function( margin ) { // Deal with component/short-hand form. var values = margin.split( ' ' ); margin = CKEDITOR.tools.convertToPx( values[ 3 ] || values[ 1 ] || values [ 0 ] ); // Figure out the indent unit by checking the first time of incrementation. if ( !listBaseIndent && previousListItemMargin !== null && margin > previousListItemMargin ) listBaseIndent = margin - previousListItemMargin; previousListItemMargin = margin; attrs[ 'cke:indent' ] = listBaseIndent && ( Math.ceil( margin / listBaseIndent ) + 1 ) || 1; } ], // The best situation: "mso-list:l0 level1 lfo2" tells the belonged list root, list item indentation, etc. [ ( /^mso-list$/ ), null, function( val ) { val = val.split( ' ' ); var listId = Number( val[ 0 ].match( /\d+/ ) ), indent = Number( val[ 1 ].match( /\d+/ ) ); if ( indent == 1 ) { listId !== previousListId && ( attrs[ 'cke:reset' ] = 1 ); previousListId = listId; } attrs[ 'cke:indent' ] = indent; } ] ] )( attrs.style, element ) || ''; } // First level list item might be presented without a margin. // In case all above doesn't apply. if ( !attrs[ 'cke:indent' ] ) { previousListItemMargin = 0; attrs[ 'cke:indent' ] = 1; } // Inherit attributes from bullet. CKEDITOR.tools.extend( attrs, listMarker.attributes ); return true; } // Current list disconnected. else previousListId = previousListItemMargin = listBaseIndent = null; return false; }, // Providing a shorthand style then retrieve one or more style component values. getStyleComponents : ( function() { var calculator = CKEDITOR.dom.element.createFromHtml( '
', CKEDITOR.document ); CKEDITOR.document.getBody().append( calculator ); return function( name, styleValue, fetchList ) { calculator.setStyle( name, styleValue ); var styles = {}, count = fetchList.length; for ( var i = 0; i < count; i++ ) styles[ fetchList[ i ] ] = calculator.getStyle( fetchList[ i ] ); return styles; }; } )(), listDtdParents : CKEDITOR.dtd.parentOf( 'ol' ) }, filters : { // Transform a normal list into flat list items only presentation. // E.g.