8
0
Просмотр исходного кода

Merge branch 'master' into t/225

Maciej Gołaszewski 7 лет назад
Родитель
Сommit
2f6f9d7939

+ 4 - 2
packages/ckeditor5-image/.travis.yml

@@ -1,7 +1,7 @@
 sudo: required
 dist: trusty
 addons:
-  firefox: "latest"
+  firefox: latest
   apt:
     sources:
     - google-chrome
@@ -9,12 +9,14 @@ addons:
     - google-chrome-stable
 language: node_js
 node_js:
-- '6'
+- '8'
 cache:
 - node_modules
 before_install:
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start
+- npm config set package-lock false
+- npm i -g npm@^5.7.1
 install:
 - npm install @ckeditor/ckeditor5-dev-tests
 - ckeditor5-dev-tests-install-dependencies

+ 1 - 1
packages/ckeditor5-image/README.md

@@ -14,7 +14,7 @@ This package implements the image feature for CKEditor 5. The feature is introdu
 
 ## Documentation
 
-See the [`@ckeditor/ckeditor5-image` package](https://docs.ckeditor.com/ckeditor5/latest/api/image.html) page in [CKEditor 5 documentation](https://docs.ckeditor.com/ckeditor5/latest/).
+See the [`@ckeditor/ckeditor5-image` package](https://ckeditor.com/docs/ckeditor5/latest/api/image.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
 
 ## License
 

+ 1 - 1
packages/ckeditor5-image/package.json

@@ -29,7 +29,7 @@
     "@ckeditor/ckeditor5-paragraph": "^10.0.2",
     "@ckeditor/ckeditor5-typing": "^11.0.0",
     "@ckeditor/ckeditor5-undo": "^10.0.2",
-    "eslint": "^4.15.0",
+    "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",
     "lint-staged": "^7.0.0"

+ 1 - 1
packages/ckeditor5-image/src/image/imageediting.js

@@ -114,7 +114,7 @@ export default class ImageEditing extends Plugin {
 // Note that `alt` and `src` attributes are converted separately, so they are not included.
 //
 // @private
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @returns {module:engine/view/containerelement~ContainerElement}
 export function createImageViewElement( writer ) {
 	const emptyElement = writer.createEmptyElement( 'img' );

+ 1 - 1
packages/ckeditor5-image/src/image/utils.js

@@ -18,7 +18,7 @@ const imageSymbol = Symbol( 'isImage' );
  * * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
  *
  * @param {module:engine/view/element~Element} viewElement
- * @param {module:engine/view/writer~Writer} writer An instance of the view writer.
+ * @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
  * @param {String} label The element's label. It will be concatenated with the image `alt` attribute if one is present.
  * @returns {module:engine/view/element~Element}
  */

+ 3 - 3
packages/ckeditor5-image/src/imagecaption/imagecaptionediting.js

@@ -88,7 +88,7 @@ export default class ImageCaptionEditing extends Plugin {
 	 * and then visible when the image is selected.
 	 *
 	 * @private
-	 * @param {module:engine/view/writer~Writer} viewWriter
+	 * @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
 	 * @returns {Boolean} Returns `true` when the view is updated.
 	 */
 	_updateCaptionVisibility( viewWriter ) {
@@ -266,7 +266,7 @@ function getParentCaption( node ) {
 //
 // @private
 // @param {module:engine/view/containerelement~ContainerElement} caption
-// @param {module:engine/view/writer~Writer} viewWriter
+// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
 // @returns {Boolean} Returns `true` if the view was modified.
 function hideCaptionIfEmpty( caption, viewWriter ) {
 	if ( !caption.childCount && !caption.hasClass( 'ck-hidden' ) ) {
@@ -281,7 +281,7 @@ function hideCaptionIfEmpty( caption, viewWriter ) {
 //
 // @private
 // @param {module:engine/view/containerelement~ContainerElement} caption
-// @param {module:engine/view/writer~Writer} viewWriter
+// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
 // @returns {Boolean} Returns `true` if the view was modified.
 function showCaption( caption, viewWriter ) {
 	if ( caption.hasClass( 'ck-hidden' ) ) {

+ 16 - 9
packages/ckeditor5-image/src/imageupload/imageuploadediting.js

@@ -16,7 +16,7 @@ import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
 import { isImageType, findOptimalInsertionPosition } from '../../src/imageupload/utils';
 
 /**
- * Image upload editing plugin.
+ * The editing part of the image upload feature.
  *
  * @extends module:core/plugin~Plugin
  */
@@ -45,7 +45,10 @@ export default class ImageUploadEditing extends Plugin {
 		// Register imageUpload command.
 		editor.commands.add( 'imageUpload', new ImageUploadCommand( editor ) );
 
-		// Execute imageUpload command when image is dropped or pasted.
+		// Handle pasted images.
+		// For every image file, a new file loader is created and a placeholder image is
+		// inserted into the content. Then, those images are uploaded once they appear in the model
+		// (see Document#change listener below).
 		this.listenTo( editor.editing.view.document, 'clipboardInput', ( evt, data ) => {
 			// Skip if non empty HTML data is included.
 			// https://github.com/ckeditor/ckeditor5-upload/issues/68
@@ -89,11 +92,12 @@ export default class ImageUploadEditing extends Plugin {
 			}
 		} );
 
-		// Prevents from browser redirecting to the dropped image.
+		// Prevents from the browser redirecting to the dropped image.
 		editor.editing.view.document.on( 'dragover', ( evt, data ) => {
 			data.preventDefault();
 		} );
 
+		// Upload placeholder images that appeared in the model.
 		doc.on( 'change', () => {
 			const changes = doc.differ.getChanges( { includeChangesInGraveyard: true } );
 
@@ -120,8 +124,8 @@ export default class ImageUploadEditing extends Plugin {
 						// If the image was inserted to the graveyard - abort the loading process.
 						loader.abort();
 					} else if ( loader.status == 'idle' ) {
-						// If the image was inserted into content and has not been loaded, start loading it.
-						this._load( loader, item );
+						// If the image was inserted into content and has not been loaded yet, start loading it.
+						this._readAndUpload( loader, item );
 					}
 				}
 			}
@@ -129,15 +133,18 @@ export default class ImageUploadEditing extends Plugin {
 	}
 
 	/**
-	 * Performs image loading. The image is read from the disk and temporary data is displayed. When the upload process
-	 * is complete the temporary data is replaced with the target image from the server.
+	 * Read and upload an image.
 	 *
-	 * @private
+	 * The image is read from the disk and as a base64 encoded string it is set temporarily to
+	 * `image[src]`. When the image is successfully uploaded the temporary data is replaced with the target
+	 * image's URL (the URL to the uploaded image on the server).
+	 *
+	 * @protected
 	 * @param {module:upload/filerepository~FileLoader} loader
 	 * @param {module:engine/model/element~Element} imageElement
 	 * @returns {Promise}
 	 */
-	_load( loader, imageElement ) {
+	_readAndUpload( loader, imageElement ) {
 		const editor = this.editor;
 		const model = editor.model;
 		const t = editor.locale.t;

+ 10 - 10
packages/ckeditor5-image/src/imageupload/imageuploadprogress.js

@@ -127,7 +127,7 @@ const placeholderSymbol = Symbol( 'placeholder' );
 // Adds ck-appear class to the image figure if one is not already applied.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 function _startAppearEffect( viewFigure, writer ) {
 	if ( !viewFigure.hasClass( 'ck-appear' ) ) {
 		writer.addClass( 'ck-appear', viewFigure );
@@ -137,7 +137,7 @@ function _startAppearEffect( viewFigure, writer ) {
 // Removes ck-appear class to the image figure if one is not already removed.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 function _stopAppearEffect( viewFigure, writer ) {
 	writer.removeClass( 'ck-appear', viewFigure );
 }
@@ -146,7 +146,7 @@ function _stopAppearEffect( viewFigure, writer ) {
 //
 // @param {String} Data-uri with a svg placeholder.
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 function _showPlaceholder( placeholder, viewFigure, writer ) {
 	if ( !viewFigure.hasClass( 'ck-image-upload-placeholder' ) ) {
 		writer.addClass( 'ck-image-upload-placeholder', viewFigure );
@@ -166,7 +166,7 @@ function _showPlaceholder( placeholder, viewFigure, writer ) {
 // Removes placeholder together with infinite progress bar on given image figure.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 function _hidePlaceholder( viewFigure, writer ) {
 	if ( viewFigure.hasClass( 'ck-image-upload-placeholder' ) ) {
 		writer.removeClass( 'ck-image-upload-placeholder', viewFigure );
@@ -179,7 +179,7 @@ function _hidePlaceholder( viewFigure, writer ) {
 // Attaches it to the file loader to update when upload percentace is changed.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @param {module:upload/filerepository~FileLoader} loader
 // @param {module:engine/view/view~View} view
 function _showProgressBar( viewFigure, writer, loader, view ) {
@@ -197,7 +197,7 @@ function _showProgressBar( viewFigure, writer, loader, view ) {
 // Hides upload progress bar.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 function _hideProgressBar( viewFigure, writer ) {
 	_removeUIElement( viewFigure, writer, progressBarSymbol );
 }
@@ -205,7 +205,7 @@ function _hideProgressBar( viewFigure, writer ) {
 // Shows complete icon and hides after a certain amount of time.
 //
 // @param {module:engine/view/containerelement~ContainerElement} viewFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @param {module:engine/view/view~View} view
 function _showCompleteIcon( viewFigure, writer, view ) {
 	const completeIcon = new UIElement( 'div', { class: 'ck-image-upload-complete-icon' } );
@@ -220,7 +220,7 @@ function _showCompleteIcon( viewFigure, writer, view ) {
 // Create progress bar element using {@link module:engine/view/uielement~UIElement}.
 //
 // @private
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @returns {module:engine/view/uielement~UIElement}
 function _createProgressBar( writer ) {
 	const progressBar = writer.createUIElement( 'div', { class: 'ck-progress-bar' } );
@@ -233,7 +233,7 @@ function _createProgressBar( writer ) {
 // Create placeholder element using {@link module:engine/view/uielement~UIElement}.
 //
 // @private
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @returns {module:engine/view/uielement~UIElement}
 function _createPlaceholder( writer ) {
 	const placeholder = writer.createUIElement( 'div', { class: 'ck-upload-placeholder-loader' } );
@@ -262,7 +262,7 @@ function _getUIElement( imageFigure, uniqueProperty ) {
 //
 // @private
 // @param {module:engine/view/element~Element} imageFigure
-// @param {module:engine/view/writer~Writer} writer
+// @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @param {Symbol} uniqueProperty
 function _removeUIElement( viewFigure, writer, uniqueProperty ) {
 	const element = _getUIElement( viewFigure, uniqueProperty );

+ 2 - 2
packages/ckeditor5-image/tests/image/utils.js

@@ -6,7 +6,7 @@
 import ViewElement from '@ckeditor/ckeditor5-engine/src/view/element';
 import ViewSelection from '@ckeditor/ckeditor5-engine/src/view/selection';
 import ViewDocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment';
-import ViewWriter from '@ckeditor/ckeditor5-engine/src/view/writer';
+import ViewDowncastWriter from '@ckeditor/ckeditor5-engine/src/view/downcastwriter';
 import ViewDocument from '@ckeditor/ckeditor5-engine/src/view/document';
 import ViewRange from '@ckeditor/ckeditor5-engine/src/view/range';
 import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
@@ -17,7 +17,7 @@ describe( 'image widget utils', () => {
 	let element, image, writer;
 
 	beforeEach( () => {
-		writer = new ViewWriter( new ViewDocument() );
+		writer = new ViewDowncastWriter( new ViewDocument() );
 		image = new ViewElement( 'img' );
 		element = new ViewElement( 'figure', null, image );
 		toImageWidget( element, writer, 'image widget' );

+ 128 - 0
packages/ckeditor5-image/tests/imagecaption/imagecaption-integration.js

@@ -0,0 +1,128 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Image from '../../src/image';
+import ImageCaption from '../../src/imagecaption';
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import DomEventData from '@ckeditor/ckeditor5-engine/src/view/observer/domeventdata';
+import Enter from '@ckeditor/ckeditor5-enter/src/enter';
+import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
+
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import global from '@ckeditor/ckeditor5-utils/src/dom/global';
+
+describe( 'ImageCaption integration', () => {
+	let editorElement, editor, model, view, viewDocument;
+
+	beforeEach( () => {
+		editorElement = global.document.createElement( 'div' );
+		global.document.body.appendChild( editorElement );
+	} );
+
+	afterEach( () => {
+		editorElement.remove();
+	} );
+
+	describe( 'with Enter plugin only', () => {
+		beforeEach( () => {
+			return ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						Enter, Typing, Paragraph, Image, ImageCaption
+					]
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+					model = editor.model;
+					view = editor.editing.view;
+					viewDocument = view.document;
+				} );
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
+		} );
+
+		it( 'does nothing if soft enter was pressed', () => {
+			setModelData(
+				model,
+				'<paragraph>Foo.</paragraph><image src="foo.png"><caption>Foo.[]</caption></image><paragraph>Bar.</paragraph>'
+			);
+
+			const domEvent = new DomEventData( viewDocument, getDomEvent(), { isSoft: true } );
+			const preventDefaultOriginal = domEvent.preventDefault;
+			const preventDefaultStub = sinon.stub( domEvent, 'preventDefault' ).callsFake( preventDefaultOriginal );
+
+			viewDocument.fire( 'enter', domEvent );
+
+			expect( preventDefaultStub.callCount ).to.equal( 1 );
+
+			assertModelData(
+				'<paragraph>Foo.</paragraph>' +
+				'<image src="foo.png">' +
+					'<caption>Foo.[]</caption>' +
+				'</image>' +
+				'<paragraph>Bar.</paragraph>'
+			);
+		} );
+	} );
+
+	describe( 'with Enter and ShiftEnter plugins', () => {
+		beforeEach( () => {
+			return ClassicEditor
+				.create( editorElement, {
+					plugins: [
+						Enter, ShiftEnter, Typing, Paragraph, Image, ImageCaption
+					]
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+					model = editor.model;
+					view = editor.editing.view;
+					viewDocument = view.document;
+				} );
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
+		} );
+
+		it( 'inserts a soft break when soft enter was pressed', () => {
+			setModelData(
+				model,
+				'<paragraph>Foo.</paragraph><image src="foo.png"><caption>Foo.[]</caption></image><paragraph>Bar.</paragraph>'
+			);
+
+			const domEvent = new DomEventData( viewDocument, getDomEvent(), { isSoft: true } );
+			const preventDefaultOriginal = domEvent.preventDefault;
+			const preventDefaultStub = sinon.stub( domEvent, 'preventDefault' ).callsFake( preventDefaultOriginal );
+
+			viewDocument.fire( 'enter', domEvent );
+
+			// One call comes from Enter plugin, the second one from ShiftEnter.
+			expect( preventDefaultStub.callCount ).to.equal( 2 );
+
+			assertModelData(
+				'<paragraph>Foo.</paragraph>' +
+					'<image src="foo.png">' +
+						'<caption>Foo.<softBreak></softBreak>[]</caption>' +
+					'</image>' +
+				'<paragraph>Bar.</paragraph>'
+			);
+		} );
+	} );
+
+	function getDomEvent() {
+		return {
+			preventDefault: sinon.spy()
+		};
+	}
+
+	function assertModelData( output ) {
+		expect( getModelData( model ) ).to.equal( output );
+	}
+} );

+ 2 - 2
packages/ckeditor5-image/tests/imagestyle/imagestylecommand.js

@@ -74,11 +74,11 @@ describe( 'ImageStyleCommand', () => {
 		setData( model, '[<image></image>]' );
 
 		model.change( writer => {
-			expect( writer.batch.deltas ).to.length( 0 );
+			expect( writer.batch.operations ).to.length( 0 );
 
 			command.execute( { value: 'otherStyle' } );
 
-			expect( writer.batch.deltas ).to.length.above( 0 );
+			expect( writer.batch.operations ).to.length.above( 0 );
 		} );
 	} );
 

+ 2 - 2
packages/ckeditor5-image/tests/imagetextalternative/imagetextalternativecommand.js

@@ -77,11 +77,11 @@ describe( 'ImageTextAlternativeCommand', () => {
 		setData( model, '[<image src="image.png"></image>]' );
 
 		model.change( writer => {
-			expect( writer.batch.deltas ).to.length( 0 );
+			expect( writer.batch.operations ).to.length( 0 );
 
 			command.execute( { newValue: 'foo bar' } );
 
-			expect( writer.batch.deltas ).to.length.above( 0 );
+			expect( writer.batch.operations ).to.length.above( 0 );
 		} );
 	} );
 } );

+ 2 - 2
packages/ckeditor5-image/tests/imageupload/imageuploadcommand.js

@@ -120,11 +120,11 @@ describe( 'ImageUploadCommand', () => {
 			setModelData( model, '<paragraph>[]foo</paragraph>' );
 
 			model.change( writer => {
-				expect( writer.batch.deltas ).to.length( 0 );
+				expect( writer.batch.operations ).to.length( 0 );
 
 				command.execute( { file } );
 
-				expect( writer.batch.deltas ).to.length.above( 0 );
+				expect( writer.batch.operations ).to.length.above( 0 );
 			} );
 		} );
 

+ 1 - 1
packages/ckeditor5-image/tests/imageupload/imageuploadediting.js

@@ -380,7 +380,7 @@ describe( 'ImageUploadEditing', () => {
 		const file = createNativeFileMock();
 		const error = new Error( 'Foo bar baz' );
 		const uploadEditing = editor.plugins.get( ImageUploadEditing );
-		const loadSpy = sinon.spy( uploadEditing, '_load' );
+		const loadSpy = sinon.spy( uploadEditing, '_readAndUpload' );
 		const catchSpy = sinon.spy();
 
 		// Throw an error when async attribute change occur.

+ 1 - 1
packages/ckeditor5-image/tests/manual/tickets/142/1.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */