Browse Source

Added first implementation of converters for image styles.

Szymon Kupś 9 years ago
parent
commit
e4253dc4a4

+ 47 - 0
packages/ckeditor5-image/src/imagestyle/imagestyle.js

@@ -0,0 +1,47 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module image/imagestyle/imagestyle
+ */
+
+import Plugin from '../../core/plugin.js';
+import ImageStyleEngine from './imagestyleengine.js';
+import ButtonView from '../../ui/button/buttonview.js';
+
+export default class ImageStyle extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ ImageStyleEngine ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+		const command = editor.commands.get( 'imagestyle' );
+
+		// Add bold button to feature components.
+		editor.ui.componentFactory.add( 'imagestyle', ( locale ) => {
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: t( 'Image style' ),
+				icon: 'bold'
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// // Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( 'imagestyle' ) );
+
+			return view;
+		} );
+	}
+}

+ 5 - 15
packages/ckeditor5-image/src/imagestyle/imagestylecommand.js

@@ -17,9 +17,8 @@ export default class ImageStyleCommand extends Command {
 		this.set( 'value', false );
 
 		const document = this.editor.document;
-
 		this.listenTo( document.selection, 'change', () => {
-			const element = getSelectedElement( document.selection );
+			const element = document.selection.getSelectedElement();
 
 			if ( element && element.name === 'image' && element.hasAttribute( 'style' ) ) {
 				this.value = element.getAttribute( 'style' );
@@ -35,22 +34,13 @@ export default class ImageStyleCommand extends Command {
 
 	_checkEnabled() {
 		const document = this.editor.document;
-		const element = getSelectedElement( document.selection );
+		const element = document.selection.getSelectedElement();
 
-		// Todo: add schema checking.
 		return element && element.name === 'image';
 	}
-}
 
-// TODO: duplicated code - move to model selection.
-function getSelectedElement( modelSelection ) {
-	if ( modelSelection.rangeCount !== 1 ) {
-		return null;
+	_doExecute() {
+		console.log( 'execute image style command' );
 	}
-
-	const range = modelSelection.getFirstRange();
-	const nodeAfterStart = range.start.nodeAfter;
-	const nodeBeforeEnd = range.end.nodeBefore;
-
-	return ( nodeAfterStart instanceof ModelElement && nodeAfterStart == nodeBeforeEnd ) ? nodeAfterStart : null;
 }
+

+ 165 - 0
packages/ckeditor5-image/src/imagestyle/imagestyleengine.js

@@ -0,0 +1,165 @@
+/**
+ * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module image/imagestyle/imagestyleengine
+ */
+
+import Plugin from '../../core/plugin.js';
+import ImageStyleCommand from './imagestylecommand.js';
+import ImageEngine from '../imageengine.js';
+import ModelElement from '../../engine/model/element.js';
+
+export default class ImageStyleEngine extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ ImageEngine ];
+	}
+
+	init() {
+		const editor = this.editor;
+		const doc = editor.document;
+		const schema = doc.schema;
+		const data = editor.data;
+		const editing = editor.editing;
+
+		// Define default configuration.
+		editor.config.define( 'image.styles', {
+			options: {
+				// This option is equal to situation when no style is applied at all.
+				imageStyleFull: { title: 'Full size image', image: 'bold', value: null },
+
+				// This represents side image.
+				imageStyleSide: { title: 'Side image', image: 'italic', value: 'side', className: 'image-style-side' }
+			}
+		} );
+
+		// Get configuration.
+		const styles = editor.config.get( 'image.styles.options' );
+
+		// Allow style attribute in image.
+		schema.allow( { name: 'image', attributes: 'style' } );
+
+		// Converters for models element style attribute.
+		editing.modelToView.on( 'addAttribute:style', addStyle( styles ) );
+		editing.modelToView.on( 'changeAttribute:style', changeStyle( styles ) );
+		editing.modelToView.on( 'removeAttribute:style', removeStyle( styles ) );
+
+		for ( let key in styles ) {
+			const style = styles[ key ];
+
+			// Converter for figure element from view to model.
+			// Create converter only for non-null values.
+			if ( style.value !== null ) {
+				data.viewToModel.on( 'element:figure', viewToModelImageStyle( style ), { priority: 'low' } );
+			}
+		}
+
+		// Register image style command.
+		editor.commands.set( 'imagestyle', new ImageStyleCommand( editor ) );
+	}
+}
+
+function addStyle( styles ) {
+	return ( event, data, consumable, conversionApi ) => {
+		// Check if we can consume, and we are adding in image.
+		if ( !consumable.consume( data.item, 'addAttribute:style' ) || !isImage( data.item ) ) {
+			return;
+		}
+
+		// Check if there is class name associated with given value.
+		const newStyle = getStyleByValue( data.attributeNewValue, styles );
+
+		// Check if new style is allowed in configuration.
+		if ( !newStyle ) {
+			return;
+		}
+
+		conversionApi.mapper.toViewElement( data.item ).addClass( newStyle.className );
+	};
+}
+
+function changeStyle( styles ) {
+	return ( event, data, consumable, conversionApi ) => {
+		if ( !consumable.consume( data.item, 'changeAttribute:style' ) || !isImage( data.item ) ) {
+			return;
+		}
+
+		// Check if there is class name associated with given value.
+		const newStyle = getStyleByValue( data.attributeNewValue, styles );
+
+		// Check if new style is allowed in configuration.
+		if ( !newStyle ) {
+			return;
+		}
+
+		const viewElement = conversionApi.mapper.toViewElement( data.item );
+		viewElement.removeClass( data.attributeOldValue );
+		viewElement.addClass( newStyle.className );
+	};
+}
+
+function removeStyle( styles ) {
+	return ( event, data, consumable, conversionApi ) => {
+		if ( !consumable.consume( data.item, 'removeAttribute:style' ) || !isImage( data.item ) ) {
+			return;
+		}
+
+		// Check if there is class name associated with given value.
+		const newStyle = getStyleByValue( data.attributeNewValue, styles );
+
+		// Check if new style is allowed in configuration.
+		if ( !newStyle ) {
+			return;
+		}
+
+		conversionApi.mapper.toViewElement( data.item ).viewElement.removeClass( data.attributeOldValue );
+	};
+}
+
+function viewToModelImageStyle( style ) {
+	return ( evt, data, consumable, conversionApi ) => {
+		const viewFigureElement = data.input;
+		const modelImageElement = data.output;
+
+		// *** Step 1: Validate conversion.
+		// Check if view element has proper class to consume.
+		if ( !consumable.test( viewFigureElement, { class: style.className } ) ) {
+			return;
+		}
+
+		// Check if figure is converted to image.
+		if ( !isImage( modelImageElement ) ) {
+			return;
+		}
+
+		// Check if image element can be placed in current context wit additional attribute.
+		const attributes = [ ...modelImageElement.getAttributeKeys(), 'style' ];
+
+		if ( !conversionApi.schema.check( { name: 'image', inside: data.context, attributes } ) ) {
+			return;
+		}
+
+		// *** Step2: Convert to model.
+		consumable.consume( viewFigureElement, { class: style.className } );
+		modelImageElement.setAttribute( 'style', style.value );
+	};
+}
+
+function isImage( modelElement ) {
+	return modelElement instanceof ModelElement && modelElement.name == 'image';
+}
+
+function getStyleByValue( value, styles ) {
+	for ( let key in styles ) {
+		const style = styles[ key ];
+
+		if ( style.value == value ) {
+			return style;
+		}
+	}
+}

+ 1 - 1
packages/ckeditor5-image/tests/manual/image.html

@@ -1,6 +1,6 @@
 <div id="editor">
 	<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla finibus consequat placerat. Vestibulum id tellus et mauris sagittis tincidunt quis id mauris. Curabitur consectetur lectus sit amet tellus mattis, non lobortis leo interdum. </p>
-	<figure class="image">
+	<figure class="image image-style-side">
 		<img src="logo.png" alt="" />
 	</figure>
 	<figure class="image">

+ 3 - 2
packages/ckeditor5-image/tests/manual/image.js

@@ -13,10 +13,11 @@ import HeadingPlugin from 'ckeditor5-heading/src/heading';
 import ImagePlugin from 'ckeditor5-image/src/image';
 import UndoPlugin from 'ckeditor5-undo/src/undo';
 import ClipboardPlugin from 'ckeditor5-clipboard/src/clipboard';
+import ImageStyle from 'ckeditor5-image/src/imagestyle/imagestyle';
 
 ClassicEditor.create( document.querySelector( '#editor' ), {
-	plugins: [ EnterPlugin, TypingPlugin, ParagraphPlugin, HeadingPlugin, ImagePlugin, UndoPlugin, ClipboardPlugin ],
-	toolbar: [ 'headings', 'undo', 'redo' ]
+	plugins: [ EnterPlugin, TypingPlugin, ParagraphPlugin, HeadingPlugin, ImagePlugin, UndoPlugin, ClipboardPlugin, ImageStyle ],
+	toolbar: [ 'headings', 'undo', 'redo', 'imagestyle' ]
 } )
 .then( editor => {
 	window.editor = editor;