│
+ // │ │
│
│
+ // │ │ │ │
+ // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
+ // │right │
│ │
+ // │ │ │ │
+ // │ │ │ │
+ // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘
+ //
+ // @param {CKEDITOR.editor}
+ // @returns {Object}
+ function widgetDef( editor ) {
+ var alignClasses = editor.config.image2_alignClasses,
+ captionedClass = editor.config.image2_captionedClass;
+
+ function deflate() {
+ if ( this.deflated )
+ return;
+
+ // Remember whether widget was focused before destroyed.
+ if ( editor.widgets.focused == this.widget )
+ this.focused = true;
+
+ editor.widgets.destroy( this.widget );
+
+ // Mark widget was destroyed.
+ this.deflated = true;
+ }
+
+ function inflate() {
+ var editable = editor.editable(),
+ doc = editor.document;
+
+ // Create a new widget. This widget will be either captioned
+ // non-captioned, block or inline according to what is the
+ // new state of the widget.
+ if ( this.deflated ) {
+ this.widget = editor.widgets.initOn( this.element, 'image', this.widget.data );
+
+ // Once widget was re-created, it may become an inline element without
+ // block wrapper (i.e. when unaligned, end not captioned). Let's do some
+ // sort of autoparagraphing here (#10853).
+ if ( this.widget.inline && !( new CKEDITOR.dom.elementPath( this.widget.wrapper, editable ).block ) ) {
+ var block = doc.createElement( editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
+ block.replace( this.widget.wrapper );
+ this.widget.wrapper.move( block );
+ }
+
+ // The focus must be transferred from the old one (destroyed)
+ // to the new one (just created).
+ if ( this.focused ) {
+ this.widget.focus();
+ delete this.focused;
+ }
+
+ delete this.deflated;
+ }
+
+ // If now widget was destroyed just update wrapper's alignment.
+ // According to the new state.
+ else {
+ setWrapperAlign( this.widget, alignClasses );
+ }
+ }
+
+ return {
+ allowedContent: getWidgetAllowedContent( editor ),
+
+ requiredContent: 'img[src,alt]',
+
+ features: getWidgetFeatures( editor ),
+
+ styleableElements: 'img figure',
+
+ // This widget converts style-driven dimensions to attributes.
+ contentTransformations: [
+ [ 'img[width]: sizeToAttribute' ]
+ ],
+
+ // This widget has an editable caption.
+ editables: {
+ caption: {
+ selector: 'figcaption',
+ allowedContent: 'br em strong sub sup u s; a[!href]'
+ }
+ },
+
+ parts: {
+ image: 'img',
+ caption: 'figcaption'
+ // parts#link defined in widget#init
+ },
+
+ // The name of this widget's dialog.
+ dialog: 'image2',
+
+ // Template of the widget: plain image.
+ template: template,
+
+ data: function() {
+ var features = this.features;
+
+ // Image can't be captioned when figcaption is disallowed (#11004).
+ if ( this.data.hasCaption && !editor.filter.checkFeature( features.caption ) )
+ this.data.hasCaption = false;
+
+ // Image can't be aligned when floating is disallowed (#11004).
+ if ( this.data.align != 'none' && !editor.filter.checkFeature( features.align ) )
+ this.data.align = 'none';
+
+ // Convert the internal form of the widget from the old state to the new one.
+ this.shiftState( {
+ widget: this,
+ element: this.element,
+ oldData: this.oldData,
+ newData: this.data,
+ deflate: deflate,
+ inflate: inflate
+ } );
+
+ // Update widget.parts.link since it will not auto-update unless widget
+ // is destroyed and re-inited.
+ if ( !this.data.link ) {
+ if ( this.parts.link )
+ delete this.parts.link;
+ } else {
+ if ( !this.parts.link )
+ this.parts.link = this.parts.image.getParent();
+ }
+
+ this.parts.image.setAttributes( {
+ src: this.data.src,
+
+ // This internal is required by the editor.
+ 'data-cke-saved-src': this.data.src,
+
+ alt: this.data.alt
+ } );
+
+ // If shifting non-captioned -> captioned, remove classes
+ // related to styles from
.
+ if ( this.oldData && !this.oldData.hasCaption && this.data.hasCaption ) {
+ for ( var c in this.data.classes )
+ this.parts.image.removeClass( c );
+ }
+
+ // Set dimensions of the image according to gathered data.
+ // Do it only when the attributes are allowed (#11004).
+ if ( editor.filter.checkFeature( features.dimension ) )
+ setDimensions( this );
+
+ // Cache current data.
+ this.oldData = CKEDITOR.tools.extend( {}, this.data );
+ },
+
+ init: function() {
+ var helpers = CKEDITOR.plugins.image2,
+ image = this.parts.image,
+ data = {
+ hasCaption: !!this.parts.caption,
+ src: image.getAttribute( 'src' ),
+ alt: image.getAttribute( 'alt' ) || '',
+ width: image.getAttribute( 'width' ) || '',
+ height: image.getAttribute( 'height' ) || '',
+
+ // Lock ratio is on by default (#10833).
+ lock: this.ready ? helpers.checkHasNaturalRatio( image ) : true
+ };
+
+ // If we used 'a' in widget#parts definition, it could happen that
+ // selected element is a child of widget.parts#caption. Since there's no clever
+ // way to solve it with CSS selectors, it's done like that. (#11783).
+ var link = image.getAscendant( 'a' );
+
+ if ( link && this.wrapper.contains( link ) )
+ this.parts.link = link;
+
+ // Depending on configuration, read style/class from element and
+ // then remove it. Removed style/class will be set on wrapper in #data listener.
+ // Note: Center alignment is detected during upcast, so only left/right cases
+ // are checked below.
+ if ( !data.align ) {
+ var alignElement = data.hasCaption ? this.element : image;
+
+ // Read the initial left/right alignment from the class set on element.
+ if ( alignClasses ) {
+ if ( alignElement.hasClass( alignClasses[ 0 ] ) ) {
+ data.align = 'left';
+ } else if ( alignElement.hasClass( alignClasses[ 2 ] ) ) {
+ data.align = 'right';
+ }
+
+ if ( data.align ) {
+ alignElement.removeClass( alignClasses[ alignmentsObj[ data.align ] ] );
+ } else {
+ data.align = 'none';
+ }
+ }
+ // Read initial float style from figure/image and then remove it.
+ else {
+ data.align = alignElement.getStyle( 'float' ) || 'none';
+ alignElement.removeStyle( 'float' );
+ }
+ }
+
+ // Update data.link object with attributes if the link has been discovered.
+ if ( editor.plugins.link && this.parts.link ) {
+ data.link = helpers.getLinkAttributesParser()( editor, this.parts.link );
+
+ // Get rid of cke_widget_* classes in data. Otherwise
+ // they might appear in link dialog.
+ var advanced = data.link.advanced;
+ if ( advanced && advanced.advCSSClasses ) {
+ advanced.advCSSClasses = CKEDITOR.tools.trim( advanced.advCSSClasses.replace( /cke_\S+/, '' ) );
+ }
+ }
+
+ // Get rid of extra vertical space when there's no caption.
+ // It will improve the look of the resizer.
+ this.wrapper[ ( data.hasCaption ? 'remove' : 'add' ) + 'Class' ]( 'cke_image_nocaption' );
+
+ this.setData( data );
+
+ // Setup dynamic image resizing with mouse.
+ // Don't initialize resizer when dimensions are disallowed (#11004).
+ if ( editor.filter.checkFeature( this.features.dimension ) && editor.config.image2_disableResizer !== true )
+ setupResizer( this );
+
+ this.shiftState = helpers.stateShifter( this.editor );
+
+ // Add widget editing option to its context menu.
+ this.on( 'contextMenu', function( evt ) {
+ evt.data.image = CKEDITOR.TRISTATE_OFF;
+
+ // Integrate context menu items for link.
+ // Note that widget may be wrapped in a link, which
+ // does not belong to that widget (#11814).
+ if ( this.parts.link || this.wrapper.getAscendant( 'a' ) )
+ evt.data.link = evt.data.unlink = CKEDITOR.TRISTATE_OFF;
+ } );
+
+ // Pass the reference to this widget to the dialog.
+ this.on( 'dialog', function( evt ) {
+ evt.data.widget = this;
+ }, this );
+ },
+
+ // Overrides default method to handle internal mutability of Image2.
+ // @see CKEDITOR.plugins.widget#addClass
+ addClass: function( className ) {
+ getStyleableElement( this ).addClass( className );
+ },
+
+ // Overrides default method to handle internal mutability of Image2.
+ // @see CKEDITOR.plugins.widget#hasClass
+ hasClass: function( className ) {
+ return getStyleableElement( this ).hasClass( className );
+ },
+
+ // Overrides default method to handle internal mutability of Image2.
+ // @see CKEDITOR.plugins.widget#removeClass
+ removeClass: function( className ) {
+ getStyleableElement( this ).removeClass( className );
+ },
+
+ // Overrides default method to handle internal mutability of Image2.
+ // @see CKEDITOR.plugins.widget#getClasses
+ getClasses: ( function() {
+ var classRegex = new RegExp( '^(' + [].concat( captionedClass, alignClasses ).join( '|' ) + ')$' );
+
+ return function() {
+ var classes = this.repository.parseElementClasses( getStyleableElement( this ).getAttribute( 'class' ) );
+
+ // Neither config.image2_captionedClass nor config.image2_alignClasses
+ // do not belong to style classes.
+ for ( var c in classes ) {
+ if ( classRegex.test( c ) )
+ delete classes[ c ];
+ }
+
+ return classes;
+ };
+ } )(),
+
+ upcast: upcastWidgetElement( editor ),
+ downcast: downcastWidgetElement( editor )
+ };
+ }
+
+ /**
+ * A set of Enhanced Image (image2) plugin helpers.
+ *
+ * @class
+ * @singleton
+ */
+ CKEDITOR.plugins.image2 = {
+ stateShifter: function( editor ) {
+ // Tag name used for centering non-captioned widgets.
+ var doc = editor.document,
+ alignClasses = editor.config.image2_alignClasses,
+ captionedClass = editor.config.image2_captionedClass,
+ editable = editor.editable(),
+
+ // The order that stateActions get executed. It matters!
+ shiftables = [ 'hasCaption', 'align', 'link' ];
+
+ // Atomic procedures, one per state variable.
+ var stateActions = {
+ align: function( shift, oldValue, newValue ) {
+ var el = shift.element;
+
+ // Alignment changed.
+ if ( shift.changed.align ) {
+ // No caption in the new state.
+ if ( !shift.newData.hasCaption ) {
+ // Changed to "center" (non-captioned).
+ if ( newValue == 'center' ) {
+ shift.deflate();
+ shift.element = wrapInCentering( editor, el );
+ }
+
+ // Changed to "non-center" from "center" while caption removed.
+ if ( !shift.changed.hasCaption && oldValue == 'center' && newValue != 'center' ) {
+ shift.deflate();
+ shift.element = unwrapFromCentering( el );
+ }
+ }
+ }
+
+ // Alignment remains and "center" removed caption.
+ else if ( newValue == 'center' && shift.changed.hasCaption && !shift.newData.hasCaption ) {
+ shift.deflate();
+ shift.element = wrapInCentering( editor, el );
+ }
+
+ // Finally set display for figure.
+ if ( !alignClasses && el.is( 'figure' ) ) {
+ if ( newValue == 'center' )
+ el.setStyle( 'display', 'inline-block' );
+ else
+ el.removeStyle( 'display' );
+ }
+ },
+
+ hasCaption: function( shift, oldValue, newValue ) {
+ // This action is for real state change only.
+ if ( !shift.changed.hasCaption )
+ return;
+
+ // Get
or
from widget. Note that widget element might itself
+ // be what we're looking for. Also element can be
...
.
+ var imageOrLink;
+ if ( shift.element.is( { img: 1, a: 1 } ) )
+ imageOrLink = shift.element;
+ else
+ imageOrLink = shift.element.findOne( 'a,img' );
+
+ // Switching hasCaption always destroys the widget.
+ shift.deflate();
+
+ // There was no caption, but the caption is to be added.
+ if ( newValue ) {
+ // Create new
from widget template.
+ var figure = CKEDITOR.dom.element.createFromHtml( templateBlock.output( {
+ captionedClass: captionedClass,
+ captionPlaceholder: editor.lang.image2.captionPlaceholder
+ } ), doc );
+
+ // Replace element with .
+ replaceSafely( figure, shift.element );
+
+ // Use old or instead of the one from the template,
+ // so we won't lose additional attributes.
+ imageOrLink.replace( figure.findOne( 'img' ) );
+
+ // Update widget's element.
+ shift.element = figure;
+ }
+
+ // The caption was present, but now it's to be removed.
+ else {
+ // Unwrap or from figure.
+ imageOrLink.replace( shift.element );
+
+ // Update widget's element.
+ shift.element = imageOrLink;
+ }
+ },
+
+ link: function( shift, oldValue, newValue ) {
+ if ( shift.changed.link ) {
+ var img = shift.element.is( 'img' ) ?
+ shift.element : shift.element.findOne( 'img' ),
+ link = shift.element.is( 'a' ) ?
+ shift.element : shift.element.findOne( 'a' ),
+ // Why deflate:
+ // If element is , it will be wrapped into ,
+ // which becomes a new widget.element.
+ // If element is , it will be unlinked
+ // so becomes a new widget.element.
+ needsDeflate = ( shift.element.is( 'a' ) && !newValue ) || ( shift.element.is( 'img' ) && newValue ),
+ newEl;
+
+ if ( needsDeflate )
+ shift.deflate();
+
+ // If unlinked the image, returned element is .
+ if ( !newValue )
+ newEl = unwrapFromLink( link );
+ else {
+ // If linked the image, returned element is .
+ if ( !oldValue )
+ newEl = wrapInLink( img, shift.newData.link );
+
+ // Set and remove all attributes associated with this state.
+ var attributes = CKEDITOR.plugins.image2.getLinkAttributesGetter()( editor, newValue );
+
+ if ( !CKEDITOR.tools.isEmpty( attributes.set ) )
+ ( newEl || link ).setAttributes( attributes.set );
+
+ if ( attributes.removed.length )
+ ( newEl || link ).removeAttributes( attributes.removed );
+ }
+
+ if ( needsDeflate )
+ shift.element = newEl;
+ }
+ }
+ };
+
+ function wrapInCentering( editor, element ) {
+ var attribsAndStyles = {};
+
+ if ( alignClasses )
+ attribsAndStyles.attributes = { 'class': alignClasses[ 1 ] };
+ else
+ attribsAndStyles.styles = { 'text-align': 'center' };
+
+ // There's no gentle way to center inline element with CSS, so create p/div
+ // that wraps widget contents and does the trick either with style or class.
+ var center = doc.createElement(
+ editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div', attribsAndStyles );
+
+ // Replace element with centering wrapper.
+ replaceSafely( center, element );
+ element.move( center );
+
+ return center;
+ }
+
+ function unwrapFromCentering( element ) {
+ var imageOrLink = element.findOne( 'a,img' );
+
+ imageOrLink.replace( element );
+
+ return imageOrLink;
+ }
+
+ // Wraps -> .
+ // Returns reference to .
+ //
+ // @param {CKEDITOR.dom.element} img
+ // @param {Object} linkData
+ // @returns {CKEDITOR.dom.element}
+ function wrapInLink( img, linkData ) {
+ var link = doc.createElement( 'a', {
+ attributes: {
+ href: linkData.url
+ }
+ } );
+
+ link.replace( img );
+ img.move( link );
+
+ return link;
+ }
+
+ // De-wraps -> .
+ // Returns the reference to
+ //
+ // @param {CKEDITOR.dom.element} link
+ // @returns {CKEDITOR.dom.element}
+ function unwrapFromLink( link ) {
+ var img = link.findOne( 'img' );
+
+ img.replace( link );
+
+ return img;
+ }
+
+ function replaceSafely( replacing, replaced ) {
+ if ( replaced.getParent() ) {
+ var range = editor.createRange();
+
+ range.moveToPosition( replaced, CKEDITOR.POSITION_BEFORE_START );
+
+ // Remove old element. Do it before insertion to avoid a case when
+ // element is moved from 'replaced' element before it, what creates
+ // a tricky case which insertElementIntorRange does not handle.
+ replaced.remove();
+
+ editable.insertElementIntoRange( replacing, range );
+ }
+ else {
+ replacing.replace( replaced );
+ }
+ }
+
+ return function( shift ) {
+ var name, i;
+
+ shift.changed = {};
+
+ for ( i = 0; i < shiftables.length; i++ ) {
+ name = shiftables[ i ];
+
+ shift.changed[ name ] = shift.oldData ?
+ shift.oldData[ name ] !== shift.newData[ name ] : false;
+ }
+
+ // Iterate over possible state variables.
+ for ( i = 0; i < shiftables.length; i++ ) {
+ name = shiftables[ i ];
+
+ stateActions[ name ]( shift,
+ shift.oldData ? shift.oldData[ name ] : null,
+ shift.newData[ name ] );
+ }
+
+ shift.inflate();
+ };
+ },
+
+ /**
+ * Checks whether the current image ratio matches the natural one
+ * by comparing dimensions.
+ *
+ * @param {CKEDITOR.dom.element} image
+ * @returns {Boolean}
+ */
+ checkHasNaturalRatio: function( image ) {
+ var $ = image.$,
+ natural = this.getNatural( image );
+
+ // The reason for two alternative comparisons is that the rounding can come from
+ // both dimensions, e.g. there are two cases:
+ // 1. height is computed as a rounded relation of the real height and the value of width,
+ // 2. width is computed as a rounded relation of the real width and the value of heigh.
+ return Math.round( $.clientWidth / natural.width * natural.height ) == $.clientHeight ||
+ Math.round( $.clientHeight / natural.height * natural.width ) == $.clientWidth;
+ },
+
+ /**
+ * Returns natural dimensions of the image. For modern browsers
+ * it uses natural(Width|Height). For old ones (IE8) it creates
+ * a new image and reads the dimensions.
+ *
+ * @param {CKEDITOR.dom.element} image
+ * @returns {Object}
+ */
+ getNatural: function( image ) {
+ var dimensions;
+
+ if ( image.$.naturalWidth ) {
+ dimensions = {
+ width: image.$.naturalWidth,
+ height: image.$.naturalHeight
+ };
+ } else {
+ var img = new Image();
+ img.src = image.getAttribute( 'src' );
+
+ dimensions = {
+ width: img.width,
+ height: img.height
+ };
+ }
+
+ return dimensions;
+ },
+
+ /**
+ * Returns an attribute getter function. Default getter comes from the Link plugin
+ * and is documented by {@link CKEDITOR.plugins.link#getLinkAttributes}.
+ *
+ * **Note:** It is possible to override this method and use a custom getter e.g.
+ * in the absence of the Link plugin.
+ *
+ * **Note:** If a custom getter is used, a data model format it produces
+ * must be compatible with {@link CKEDITOR.plugins.link#getLinkAttributes}.
+ *
+ * **Note:** A custom getter must understand the data model format produced by
+ * {@link #getLinkAttributesParser} to work correctly.
+ *
+ * @returns {Function} A function that gets (composes) link attributes.
+ * @since 4.5.5
+ */
+ getLinkAttributesGetter: function() {
+ // #13885
+ return CKEDITOR.plugins.link.getLinkAttributes;
+ },
+
+ /**
+ * Returns an attribute parser function. Default parser comes from the Link plugin
+ * and is documented by {@link CKEDITOR.plugins.link#parseLinkAttributes}.
+ *
+ * **Note:** It is possible to override this method and use a custom parser e.g.
+ * in the absence of the Link plugin.
+ *
+ * **Note:** If a custom parser is used, a data model format produced by the parser
+ * must be compatible with {@link #getLinkAttributesGetter}.
+ *
+ * **Note:** If a custom parser is used, it should be compatible with the
+ * {@link CKEDITOR.plugins.link#parseLinkAttributes} data model format. Otherwise the
+ * Link plugin dialog may not be populated correctly with parsed data. However
+ * as long as Enhanced Image is **not** used with the Link plugin dialog, any custom data model
+ * will work, being stored as an internal property of Enhanced Image widget's data only.
+ *
+ * @returns {Function} A function that parses attributes.
+ * @since 4.5.5
+ */
+ getLinkAttributesParser: function() {
+ // #13885
+ return CKEDITOR.plugins.link.parseLinkAttributes;
+ }
+ };
+
+ function setWrapperAlign( widget, alignClasses ) {
+ var wrapper = widget.wrapper,
+ align = widget.data.align,
+ hasCaption = widget.data.hasCaption;
+
+ if ( alignClasses ) {
+ // Remove all align classes first.
+ for ( var i = 3; i--; )
+ wrapper.removeClass( alignClasses[ i ] );
+
+ if ( align == 'center' ) {
+ // Avoid touching non-captioned, centered widgets because
+ // they have the class set on the element instead of wrapper:
+ //
+ //
+ if ( hasCaption ) {
+ wrapper.addClass( alignClasses[ 1 ] );
+ }
+ } else if ( align != 'none' ) {
+ wrapper.addClass( alignClasses[ alignmentsObj[ align ] ] );
+ }
+ } else {
+ if ( align == 'center' ) {
+ if ( hasCaption )
+ wrapper.setStyle( 'text-align', 'center' );
+ else
+ wrapper.removeStyle( 'text-align' );
+
+ wrapper.removeStyle( 'float' );
+ }
+ else {
+ if ( align == 'none' )
+ wrapper.removeStyle( 'float' );
+ else
+ wrapper.setStyle( 'float', align );
+
+ wrapper.removeStyle( 'text-align' );
+ }
+ }
+ }
+
+ // Returns a function that creates widgets from all and
+ // elements.
+ //
+ // @param {CKEDITOR.editor} editor
+ // @returns {Function}
+ function upcastWidgetElement( editor ) {
+ var isCenterWrapper = centerWrapperChecker( editor ),
+ captionedClass = editor.config.image2_captionedClass;
+
+ // @param {CKEDITOR.htmlParser.element} el
+ // @param {Object} data
+ return function( el, data ) {
+ var dimensions = { width: 1, height: 1 },
+ name = el.name,
+ image;
+
+ // #11110 Don't initialize on pasted fake objects.
+ if ( el.attributes[ 'data-cke-realelement' ] )
+ return;
+
+ // If a center wrapper is found, there are 3 possible cases:
+ //
+ // 1. ...
.
+ // In this case centering is done with a class set on widget.wrapper.
+ // Simply replace centering wrapper with figure (it's no longer necessary).
+ //
+ // 2.
.
+ // Nothing to do here: remains for styling purposes.
+ //
+ // 3.
.
+ // Nothing to do here (2.) but that case is only possible in enterMode different
+ // than ENTER_P.
+ if ( isCenterWrapper( el ) ) {
+ if ( name == 'div' ) {
+ var figure = el.getFirst( 'figure' );
+
+ // Case #1.
+ if ( figure ) {
+ el.replaceWith( figure );
+ el = figure;
+ }
+ }
+ // Cases #2 and #3 (handled transparently)
+
+ // If there's a centering wrapper, save it in data.
+ data.align = 'center';
+
+ // Image can be wrapped in link .
+ image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
+ }
+
+ // No center wrapper has been found.
+ else if ( name == 'figure' && el.hasClass( captionedClass ) ) {
+ image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
+
+ // Upcast linked image like .
+ } else if ( isLinkedOrStandaloneImage( el ) ) {
+ image = el.name == 'a' ? el.children[ 0 ] : el;
+ }
+
+ if ( !image )
+ return;
+
+ // If there's an image, then cool, we got a widget.
+ // Now just remove dimension attributes expressed with %.
+ for ( var d in dimensions ) {
+ var dimension = image.attributes[ d ];
+
+ if ( dimension && dimension.match( regexPercent ) )
+ delete image.attributes[ d ];
+ }
+
+ return el;
+ };
+ }
+
+ // Returns a function which transforms the widget to the external format
+ // according to the current configuration.
+ //
+ // @param {CKEDITOR.editor}
+ function downcastWidgetElement( editor ) {
+ var alignClasses = editor.config.image2_alignClasses;
+
+ // @param {CKEDITOR.htmlParser.element} el
+ return function( el ) {
+ // In case of , is the element to hold
+ // inline styles or classes (image2_alignClasses).
+ var attrsHolder = el.name == 'a' ? el.getFirst() : el,
+ attrs = attrsHolder.attributes,
+ align = this.data.align;
+
+ // De-wrap the image from resize handle wrapper.
+ // Only block widgets have one.
+ if ( !this.inline ) {
+ var resizeWrapper = el.getFirst( 'span' );
+
+ if ( resizeWrapper )
+ resizeWrapper.replaceWith( resizeWrapper.getFirst( { img: 1, a: 1 } ) );
+ }
+
+ if ( align && align != 'none' ) {
+ var styles = CKEDITOR.tools.parseCssText( attrs.style || '' );
+
+ // When the widget is captioned () and internally centering is done
+ // with widget's wrapper style/class, in the external data representation,
+ // must be wrapped with an element holding an style/class:
+ //
+ //