/**
* @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
(function() {
var template = '',
templateBlock = '',
regexPercent = /^\s*(\d+\%)\s*$/i;
CKEDITOR.plugins.add( 'image2', {
lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
requires: 'widget,dialog',
icons: 'image2',
hidpi: true,
onLoad: function( editor ) {
CKEDITOR.addCss(
'.cke_editable.cke_image2_sw, .cke_editable.cke_image2_sw *{cursor:sw-resize !important}' +
'.cke_editable.cke_image2_se, .cke_editable.cke_image2_se *{cursor:se-resize !important}' +
'.cke_image2_resizer{' +
'display:none;' +
'position:absolute;' +
'width:10px;' +
'height:10px;' +
'bottom:-5px;' +
'right:-5px;' +
'background:#000;' +
'outline:1px solid #fff;' +
'cursor:se-resize;' +
'}' +
'.cke_image2_resizer_wrapper{' +
'position:relative;' +
'display:inline-block;' +
'line-height:0;' +
'}' +
// Bottom-left corner style of the resizer.
'.cke_image2_resizer.cke_image2_resizer_left{' +
'right:auto;' +
'left:-5px;' +
'cursor:sw-resize;' +
'}' +
'.cke_widget_wrapper:hover .cke_image2_resizer,' +
'.cke_image2_resizer.cke_image2_resizing{' +
'display:block' +
'}' );
},
init: function( editor ) {
// Adapts configuration from original image plugin. Should be removed
// when we'll rename image2 to image.
var config = editor.config,
lang = editor.lang.image2;
config.filebrowserImage2BrowseUrl = config.filebrowserImageBrowseUrl;
config.filebrowserImage2UploadUrl = config.filebrowserImageUploadUrl;
// Add custom elementspath names to widget definition.
image2.pathName = lang.pathName;
image2.editables.caption.pathName = lang.pathNameCaption;
// Register the widget.
editor.widgets.add( 'image2', image2 );
// Add toolbar button for this plugin.
editor.ui.addButton && editor.ui.addButton( 'image2', {
label: editor.lang.common.image,
command: 'image2',
toolbar: 'insert,10'
} );
// Register context menu option for editing widget.
if ( editor.contextMenu ) {
editor.addMenuGroup( 'image2', 10 );
editor.addMenuItem( 'image2', {
label: lang.menu,
command: 'image2',
group: 'image2'
} );
}
CKEDITOR.dialog.add( 'image2', this.path + 'dialogs/image2.js' );
},
afterInit: function( editor ) {
var align = { left:1,right:1,center:1,block:1 },
integrate = alignCommandIntegrator( editor );
for ( var value in align )
integrate( value );
}
} );
// Image2 widget definition.
var image2 = {
// Widget-specific rules for Allowed Content Filter.
allowedContent: {
// This widget may need
centering wrapper.
div: {
match: isCenterWrapper,
styles: 'text-align'
},
figcaption: true,
figure: {
classes: '!caption',
styles: 'float,display'
},
img: {
attributes: '!src,alt,width,height',
styles: 'float'
},
// This widget may need
centering wrapper.
p: {
match: isCenterWrapper,
styles: 'text-align'
}
},
// 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'
},
// The name of this widget's dialog.
dialog: 'image2',
// Template of the widget: plain image.
template: template,
data: function() {
var widget = this,
editor = widget.editor,
oldState = widget.oldData,
newState = widget.data;
// Convert the internal form of the widget from the old state to the new one.
widget.shiftState( {
element: widget.element,
oldState: oldState,
newState: newState,
// Destroy the widget.
destroy: function() {
if ( this.destroyed )
return;
// Remember whether widget was focused before destroyed.
if ( editor.widgets.focused == widget )
this.focused = true;
editor.widgets.destroy( widget );
// Mark widget was destroyed.
this.destroyed = true;
},
init: function( element ) {
// 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.destroyed ) {
widget = editor.widgets.initOn( element, 'image2', widget.data );
// The focus must be transferred from the old one (destroyed)
// to the new one (just created).
if ( this.focused ) {
widget.focus();
delete this.focused;
}
delete this.destroyed;
}
// If now widget was destroyed just update wrapper's alignment.
// According to the new state.
else
setWrapperAlign( widget );
}
} );
widget.parts.image.setAttributes( {
src: widget.data.src,
// This internal is required by the editor.
'data-cke-saved-src': widget.data.src,
alt: widget.data.alt
} );
// Set dimensions of the image according to gathered data.
setDimensions( widget );
// Cache current data.
widget.oldData = CKEDITOR.tools.extend( {}, widget.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
};
// Read initial float style from figure/image and
// then remove it. This style will be set on wrapper in #data listener.
if ( !data.align ) {
data.align = this.element.getStyle( 'float' ) || image.getStyle( 'float' ) || 'none';
this.element.removeStyle( 'float' );
image.removeStyle( 'float' );
}
// Get rid of extra vertical space when there's no caption.
// It will improve the look of the resizer.
if ( !data.hasCaption )
this.wrapper.setStyle( 'line-height', '0' );
this.setData( data );
// Setup dynamic image resizing with mouse.
setupResizer( this );
this.shiftState = helpers.stateShifter( this.editor );
// Add widget editing option to its context menu.
this.on( 'contextMenu', function( evt ) {
evt.data.image2 = CKEDITOR.TRISTATE_OFF;
} );
// Pass the reference to this widget to the dialog.
this.on( 'dialog', function( evt ) {
evt.data.widget = this;
}, this );
},
upcast: upcastWidgetElement,
downcast: downcastWidgetElement
};
CKEDITOR.plugins.image2 = {
stateShifter: function( editor ) {
// Tag name used for centering non-captioned widgets.
var centerElement = editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div',
doc = editor.document,
editable = editor.editable(),
// The order that stateActions get executed. It matters!
shiftables = [ 'hasCaption', 'align' ],
// Atomic procedures, one per state variable.
stateActions = {
align: function( data, oldValue, newValue ) {
var hasCaptionAfter = data.newState.hasCaption,
element = data.element;
// Alignment changed.
if ( changed( data, 'align' ) ) {
// No caption in the new state.
if ( !hasCaptionAfter ) {
// Changed to "center" (non-captioned).
if ( newValue == 'center' ) {
data.destroy();
data.element = wrapInCentering( element );
}
// Changed to "non-center" from "center" while caption removed.
if ( !changed( data, 'hasCaption' ) && oldValue == 'center' && newValue != 'center' ) {
data.destroy();
data.element = unwrapFromCentering( element );
}
}
}
// Alignment remains and "center" removed caption.
else if ( newValue == 'center' && changed( data, 'hasCaption' ) && !hasCaptionAfter ) {
data.destroy();
data.element = wrapInCentering( element );
}
// Finally set display for figure.
if ( element.is( 'figure' ) ) {
if ( newValue == 'center' )
element.setStyle( 'display', 'inline-block' );
else
element.removeStyle( 'display' );
}
},
hasCaption: function( data, oldValue, newValue ) {
// This action is for real state change only.
if ( !changed( data, 'hasCaption' ) )
return;
var element = data.element,
oldState = data.oldState,
newState = data.newState,
img;
// Switching hasCaption always destroys the widget.
data.destroy();
// There was no caption, but the caption is to be added.
if ( newValue ) {
// Get from element. As element may be either
// or centering
, consider it now.
img = element.findOne( 'img' ) || element;
// Create new