/* Copyright (c) 2003-2010, 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; }; 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 listBaseIndent = 0, previousListItemMargin; CKEDITOR.plugins.pastefromword = { utils : { // Create a which indicate an list item type. createListBulletMarker : function ( bulletStyle, bulletText ) { var marker = new CKEDITOR.htmlParser.element( 'cke:listbullet' ), listType; // TODO: Support more list style type from MS-Word. if ( !bulletStyle ) { bulletStyle = 'decimal'; listType = 'ol'; } else if ( bulletStyle[ 2 ] ) { if ( !isNaN( bulletStyle[ 1 ] ) ) bulletStyle = 'decimal'; // No way to distinguish between Roman numerals and Alphas, // detect them as a whole. else if ( /^[a-z]+$/.test( bulletStyle[ 1 ] ) ) bulletStyle = 'lower-alpha'; else if ( /^[A-Z]+$/.test( bulletStyle[ 1 ] ) ) bulletStyle = 'upper-alpha'; // Simply use decimal for the rest forms of unrepresentable // numerals, e.g. Chinese... else bulletStyle = 'decimal'; listType = 'ol'; } else { if ( /[l\u00B7\u2002]/.test( bulletStyle[ 1 ] ) ) bulletStyle = 'disc'; else if ( /[\u006F\u00D8]/.test( bulletStyle[ 1 ] ) ) bulletStyle = 'circle'; else if ( /[\u006E\u25C6]/.test( bulletStyle[ 1 ] ) ) bulletStyle = 'square'; else bulletStyle = 'disc'; listType = 'ul'; } // Represent list type as CSS style. marker.attributes = { 'cke:listtype' : listType, 'style' : 'list-style-type:' + bulletStyle + ';' }; 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 = CKEDITOR.plugins.pastefromword.filters.stylesFilter( [ // Text-indent is not representing list item level any more. [ 'text-indent' ], [ 'line-height' ], // Resolve indent level from 'margin-left' value. [ ( /^margin(:?-left)?$/ ), null, function( margin ) { // Be able to deal with component/short-hand form style. var values = margin.split( ' ' ); margin = CKEDITOR.plugins.pastefromword.utils.convertToPx( values[ 3 ] || values[ 1 ] || values [ 0 ] ); margin = parseInt( margin, 10 ); // Figure out the indent unit by looking at the first increament. if ( !listBaseIndent && previousListItemMargin && margin > previousListItemMargin ) listBaseIndent = margin - previousListItemMargin; attrs[ 'cke:margin' ] = previousListItemMargin = margin; } ] ] )( attrs.style, element ) || '' ; } // Inherit list-type-style from bullet. var listBulletAttrs = listMarker.attributes, listBulletStyle = listBulletAttrs.style; element.addStyle( listBulletStyle ); CKEDITOR.tools.extend( attrs, listBulletAttrs ); return true; } return false; }, // Convert various length units to 'px' in ignorance of DPI. convertToPx : ( function () { var calculator = CKEDITOR.dom.element.createFromHtml( '
', CKEDITOR.document ); CKEDITOR.document.getBody().append( calculator ); return function( cssLength ) { if ( cssLengthRelativeUnit.test( cssLength ) ) { calculator.setStyle( 'width', cssLength ); return calculator.$.clientWidth + 'px'; } return cssLength; }; } )(), // 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.