8
0
Piotrek Koszuliński 8 лет назад
Сommit
83d2a2ef51

+ 12 - 0
packages/ckeditor5-easy-image/.editorconfig

@@ -0,0 +1,12 @@
+# Configurations to normalize the IDE behavior.
+# http://editorconfig.org/
+
+root = true
+
+[*]
+indent_style = tab
+tab_width = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true

+ 12 - 0
packages/ckeditor5-easy-image/.eslintrc.js

@@ -0,0 +1,12 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* eslint-env node */
+
+'use strict';
+
+module.exports = {
+	extends: 'ckeditor5'
+};

+ 18 - 0
packages/ckeditor5-easy-image/.gitattributes

@@ -0,0 +1,18 @@
+*			text=auto
+
+*.htaccess	eol=lf
+*.cgi		eol=lf
+*.sh		eol=lf
+
+*.css		text
+*.htm		text
+*.html		text
+*.js		text
+*.json		text
+*.php		text
+*.txt		text
+*.md		text
+
+*.png		-text
+*.gif		-text
+*.jpg		-text

+ 5 - 0
packages/ckeditor5-easy-image/.gitignore

@@ -0,0 +1,5 @@
+# These files will be ignored by Git and by our linting tools:
+#	gulp lint
+#	gulp lint-staged
+
+node_modules/

+ 24 - 0
packages/ckeditor5-easy-image/.travis.yml

@@ -0,0 +1,24 @@
+sudo: required
+dist: trusty
+addons:
+  apt:
+    sources:
+      - google-chrome
+    packages:
+      - google-chrome-stable
+language: node_js
+node_js:
+  - "6"
+cache:
+  - node_modules
+before_install:
+  - export DISPLAY=:99.0
+  - sh -e /etc/init.d/xvfb start
+install:
+  - npm install @ckeditor/ckeditor5-dev-tests
+  - ckeditor5-dev-tests-install-dependencies
+script:
+  - ckeditor5-dev-tests-travis
+after_success:
+  - codeclimate-test-reporter < coverage/lcov.info
+  - ckeditor5-dev-tests-save-revision

+ 4 - 0
packages/ckeditor5-easy-image/CONTRIBUTING.md

@@ -0,0 +1,4 @@
+Contributing
+========================================
+
+Information about contributing can be found on the following page: <https://github.com/ckeditor/ckeditor5/blob/master/CONTRIBUTING.md>.

+ 23 - 0
packages/ckeditor5-easy-image/LICENSE.md

@@ -0,0 +1,23 @@
+Software License Agreement
+==========================
+
+**CKEditor 5 Easy Image** – https://github.com/ckeditor/ckeditor5-easy-image <br>
+Copyright (c) 2003-2017, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+
+Licensed under the terms of any of the following licenses at your choice:
+
+* [GNU General Public License Version 2 or later (the "GPL")](http://www.gnu.org/licenses/gpl.html)
+* [GNU Lesser General Public License Version 2.1 or later (the "LGPL")](http://www.gnu.org/licenses/lgpl.html)
+* [Mozilla Public License Version 1.1 or later (the "MPL")](http://www.mozilla.org/MPL/MPL-1.1.html)
+
+You are not required to, but if you want to explicitly declare the license you have chosen to be bound to when using, reproducing, modifying and distributing this software, just include a text file titled "legal.txt" in your version of this software, indicating your license choice. In any case, your choice will not restrict any recipient of your version of this software to use, reproduce, modify and distribute this software under any of the above licenses.
+
+Sources of Intellectual Property Included in CKEditor
+-----------------------------------------------------
+
+Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
+
+Trademarks
+----------
+
+**CKEditor** is a trademark of [CKSource](http://cksource.com) Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.

+ 15 - 0
packages/ckeditor5-easy-image/README.md

@@ -0,0 +1,15 @@
+CKEditor 5 Easy Image with Cloud Services
+=========================================
+
+[![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-easy-image.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-easy-image)
+[![Build Status](https://travis-ci.org/ckeditor/ckeditor5-easy-image.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-easy-image)
+[![Test Coverage](https://codeclimate.com/github/ckeditor/ckeditor5-easy-image/badges/coverage.svg)](https://codeclimate.com/github/ckeditor/ckeditor5-easy-image/coverage)
+[![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-easy-image/status.svg)](https://david-dm.org/ckeditor/ckeditor5-easy-image)
+[![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-easy-image/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-easy-image?type=dev)
+
+Easy Image feature for CKEditor 5. Plugin which lets you easily insert images into the editor and integrates automatically with CKEditor Cloud Services. More information about the project can be found at the following URL: <https://github.com/ckeditor/ckeditor5-easy-image>.
+
+## License
+
+Licensed under the GPL, LGPL and MPL licenses, at your choice. For full details about the license, please check the `LICENSE.md` file.

+ 22 - 0
packages/ckeditor5-easy-image/gulpfile.js

@@ -0,0 +1,22 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* eslint-env node */
+
+'use strict';
+
+const gulp = require( 'gulp' );
+const ckeditor5Lint = require( '@ckeditor/ckeditor5-dev-lint' );
+const options = {
+	// Files ignored by `gulp lint` task.
+	// Files from .gitignore will be added automatically during task execution.
+	ignoredFiles: [
+		'src/lib/**'
+	]
+};
+
+gulp.task( 'lint', () => ckeditor5Lint.lint( options ) );
+gulp.task( 'lint-staged', () => ckeditor5Lint.lintStaged( options ) );
+gulp.task( 'pre-commit', [ 'lint-staged' ] );

+ 41 - 0
packages/ckeditor5-easy-image/package.json

@@ -0,0 +1,41 @@
+{
+  "name": "@ckeditor/ckeditor5-easy-image",
+  "version": "0.0.1",
+  "description": "Easy Image feature for CKEditor 5.",
+  "keywords": [
+    "ckeditor5",
+    "ckeditor5-feature"
+  ],
+  "dependencies": {
+    "@ckeditor/ckeditor-cloudservices-core": "^0.1.0",
+    "@ckeditor/ckeditor5-core": "^0.9.0",
+    "@ckeditor/ckeditor5-image": "^0.7.0",
+    "@ckeditor/ckeditor5-upload": "^0.2.0"
+  },
+  "devDependencies": {
+    "@ckeditor/ckeditor5-dev-lint": "^3.1.0",
+    "@ckeditor/ckeditor5-editor-classic": "^0.8.0",
+    "@ckeditor/ckeditor5-paragraph": "^0.9.0",
+    "@ckeditor/ckeditor5-utils": "^0.10.0",
+    "eslint-config-ckeditor5": "^1.0.6",
+    "gulp": "^3.9.1",
+    "guppy-pre-commit": "^0.4.0"
+  },
+  "engines": {
+    "node": ">=6.0.0",
+    "npm": ">=3.0.0"
+  },
+  "author": "CKSource (http://cksource.com/)",
+  "license": "(GPL-2.0 OR LGPL-2.1 OR MPL-1.1)",
+  "homepage": "https://ckeditor5.github.io",
+  "bugs": "https://github.com/ckeditor/ckeditor5-easy-image/issues",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/ckeditor/ckeditor5-easy-image.git"
+  },
+  "files": [
+    "lang",
+    "src",
+    "theme"
+  ]
+}

+ 64 - 0
packages/ckeditor5-easy-image/src/cloudservicesuploadadapter.js

@@ -0,0 +1,64 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+* @module easy-image/cloudservicesuploadadapter
+*/
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
+import UploadGateway from '@ckeditor/ckeditor-cloudservices-core/src/uploadgateway/uploadgateway';
+
+export default class CloudServicesUploadAdapter extends Plugin {
+	static get requires() {
+		return [ FileRepository ];
+	}
+
+	init() {
+		const editor = this.editor;
+		const config = editor.config;
+
+		const token = config.get( 'cloudServices.token' );
+		const uploadUrl = config.get( 'cloudServices.uploadUrl' );
+
+		// Application is in offline mode.
+		if ( !token || !uploadUrl ) {
+			return;
+		}
+
+		this._uploadGateway = new CloudServicesUploadAdapter._UploadGateway( token, uploadUrl );
+
+		editor.plugins.get( FileRepository ).createAdapter = loader => {
+			return new Adapter( this._uploadGateway, loader );
+		};
+	}
+}
+
+class Adapter {
+	constructor( uploadGateway, loader ) {
+		this.uploadGateway = uploadGateway;
+
+		this.loader = loader;
+	}
+
+	upload() {
+		this.fileUploader = this.uploadGateway.upload( this.loader.file );
+
+		this.fileUploader.on( 'progress', ( evt, data ) => {
+			this.loader.uploadTotal = data.total;
+			this.loader.uploaded = data.uploaded;
+		} );
+
+		return this.fileUploader.send();
+	}
+
+	abort() {
+		this.fileUploader.abort();
+	}
+}
+
+// Store the API in static property to easily overwrite it in tests.
+// Too bad dependency injection does not work in Webpack + ES 6 (const) + Babel.
+CloudServicesUploadAdapter._UploadGateway = UploadGateway;

+ 33 - 0
packages/ckeditor5-easy-image/src/easyimage.js

@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module easy-image/easyimage
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import CloudServicesUploadAdapter from './cloudservicesuploadadapter';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageUpload from '@ckeditor/ckeditor5-upload/src/imageupload';
+
+export default class EasyImage extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [
+			CloudServicesUploadAdapter,
+			Image,
+			ImageUpload
+		];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'EasyImage';
+	}
+}

+ 50 - 0
packages/ckeditor5-easy-image/tests/_utils/uploadgatewaymock.js

@@ -0,0 +1,50 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import mix from '@ckeditor/ckeditor5-utils/src/mix';
+import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
+
+export default class UploadGatewayMock {
+	constructor( token, uploadUrl ) {
+		UploadGatewayMock.lastToken = token;
+		UploadGatewayMock.lastUploadUrl = uploadUrl;
+	}
+
+	upload() {
+		this.lastFileUploader = new FileUploader();
+
+		return this.lastFileUploader;
+	}
+
+	resolveLastUpload() {
+		this.lastFileUploader._resolve( { default: 'http://image.mock.url/' } );
+	}
+}
+
+class FileUploader {
+	onProgress( callback ) {
+		this.on( 'progress', ( event, data ) => callback( data ) );
+
+		return this;
+	}
+
+	onError( callback ) {
+		this.once( 'error', ( event, data ) => callback( data ) );
+
+		return this;
+	}
+
+	send() {
+		return new Promise( resolve => {
+			this._resolve = resolve;
+		} );
+	}
+
+	abort() {
+		this.aborted = true;
+	}
+}
+
+mix( FileUploader, EmitterMixin );

+ 128 - 0
packages/ckeditor5-easy-image/tests/cloudservicesuploadadapter.js

@@ -0,0 +1,128 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals window, setTimeout */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import CloudServicesUploadAdapter from '../src/cloudservicesuploadadapter';
+import FileRepository from '@ckeditor/ckeditor5-upload/src/filerepository';
+import UploadGatewayMock from './_utils/uploadgatewaymock';
+import { createNativeFileMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
+
+// Store original uploader.
+const CSUploader = CloudServicesUploadAdapter._UploadGateway;
+
+describe( 'CloudServicesUploadAdapter', () => {
+	let div;
+
+	before( () => {
+		// Mock uploader.
+		CloudServicesUploadAdapter._UploadGateway = UploadGatewayMock;
+	} );
+
+	after( () => {
+		// Restore original uploader.
+		CloudServicesUploadAdapter._UploadGateway = CSUploader;
+	} );
+
+	beforeEach( () => {
+		div = window.document.createElement( 'div' );
+		window.document.body.appendChild( div );
+	} );
+
+	afterEach( () => {
+		window.document.body.removeChild( div );
+	} );
+
+	describe( 'init', () => {
+		it( 'should set loader', () => {
+			UploadGatewayMock.lastToken = undefined;
+
+			return ClassicTestEditor.create( div, {
+				plugins: [ CloudServicesUploadAdapter ],
+				cloudServices: {
+					documentId: 'app',
+					token: 'abc',
+					uploadUrl: 'http://upload.mock.url/'
+				}
+			} ).then( () => {
+				expect( UploadGatewayMock.lastToken ).to.equal( 'abc' );
+				expect( UploadGatewayMock.lastUploadUrl ).to.equal( 'http://upload.mock.url/' );
+			} );
+		} );
+
+		it( 'should not set loader if there is no token', () => {
+			UploadGatewayMock.lastToken = undefined;
+
+			return ClassicTestEditor.create( div, {
+				plugins: [ CloudServicesUploadAdapter ]
+			} ).then( () => {
+				expect( UploadGatewayMock.lastToken ).to.be.an( 'undefined' );
+			} );
+		} );
+	} );
+
+	describe( 'Adapter', () => {
+		let editor, fileRepository, upload;
+
+		beforeEach( () => {
+			return ClassicTestEditor.create( div, {
+				plugins: [ CloudServicesUploadAdapter ],
+				cloudServices: {
+					token: 'abc',
+					uploadUrl: 'http://upload.mock.url/'
+				}
+			} ).then( _editor => {
+				editor = _editor;
+				fileRepository = editor.plugins.get( FileRepository );
+				upload = editor.plugins.get( CloudServicesUploadAdapter );
+			} );
+		} );
+
+		afterEach( () => {
+			return editor.destroy();
+		} );
+
+		describe( 'upload', () => {
+			it( 'should mock upload', done => {
+				const loader = fileRepository.createLoader( createNativeFileMock() );
+
+				loader.upload()
+					.then( response => {
+						expect( response.default ).to.equal( 'http://image.mock.url/' );
+						done();
+					} )
+					.catch( err => done( err ) );
+
+				// Wait for the promise from the mock.getUploader()
+				setTimeout( () => {
+					upload._uploadGateway.resolveLastUpload();
+				} );
+			} );
+
+			it( 'should update the progress', () => {
+				const loader = fileRepository.createLoader( createNativeFileMock() );
+				loader.upload();
+
+				upload._uploadGateway.lastFileUploader.fire( 'progress', { uploaded: 50, total: 100 } );
+
+				// expect( loader.uploaded ).to.equal( 50 );
+				expect( loader.uploadTotal ).to.equal( 100 );
+			} );
+		} );
+
+		describe( 'abort', () => {
+			it( 'should call abort on the CSS uploader', () => {
+				const loader = fileRepository.createLoader( createNativeFileMock() );
+
+				loader.upload();
+
+				loader.abort();
+
+				expect( upload._uploadGateway.lastFileUploader.aborted ).to.be.true;
+			} );
+		} );
+	} );
+} );

+ 108 - 0
packages/ckeditor5-easy-image/tests/easyimage.js

@@ -0,0 +1,108 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals window, setTimeout */
+
+import EasyImage from '../src/easyimage';
+import CloudServicesUploadAdapter from '../src/cloudservicesuploadadapter';
+import Image from '@ckeditor/ckeditor5-image/src/image';
+import ImageUpload from '@ckeditor/ckeditor5-upload/src/imageupload';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import UploadGatewayMock from './_utils/uploadgatewaymock';
+import { createNativeFileMock } from '@ckeditor/ckeditor5-upload/tests/_utils/mocks';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
+describe( 'EasyImage', () => {
+	it( 'should require other plugins', () => {
+		const plugins = EasyImage.requires;
+
+		expect( plugins ).to.include( CloudServicesUploadAdapter );
+		expect( plugins ).to.include( Image );
+		expect( plugins ).to.include( ImageUpload );
+	} );
+
+	it( 'should be able to initialize editor with itself', () => {
+		const div = window.document.createElement( 'div' );
+		window.document.body.appendChild( div );
+
+		return ClassicTestEditor.create( div, {
+			plugins: [ EasyImage ]
+		} ).then( editor => {
+			const easyImage = editor.plugins.get( EasyImage );
+			expect( easyImage ).to.be.an.instanceOf( EasyImage );
+
+			window.document.body.removeChild( div );
+		} );
+	} );
+
+	describe( 'integration tests', () => {
+		const CSUploader = CloudServicesUploadAdapter._UploadGateway;
+		const sandbox = sinon.sandbox.create();
+		let div;
+
+		before( () => {
+			// Mock uploader.
+			CloudServicesUploadAdapter._UploadGateway = UploadGatewayMock;
+			sandbox.stub( window, 'FileReader' ).callsFake( () => {
+				const reader = {
+					readAsDataURL: () => {
+						reader.result = 'http://some-fake-url.jpg';
+						reader.onload();
+					}
+				};
+				return reader;
+			} );
+		} );
+
+		after( () => {
+			// Restore original uploader.
+			CloudServicesUploadAdapter._UploadGateway = CSUploader;
+			sandbox.restore();
+		} );
+
+		beforeEach( () => {
+			div = window.document.createElement( 'div' );
+			window.document.body.appendChild( div );
+		} );
+
+		afterEach( () => {
+			window.document.body.removeChild( div );
+		} );
+
+		it( 'should enable easy image uploading', () => {
+			return ClassicTestEditor.create( div, {
+				plugins: [
+					Paragraph, EasyImage
+				],
+				cloudServices: {
+					token: 'abc',
+					uploadUrl: 'http://upload.mock.url/'
+				}
+			} ).then( editor => {
+				const notification = editor.plugins.get( 'Notification' );
+				const upload = editor.plugins.get( CloudServicesUploadAdapter );
+
+				return new Promise( ( resolve, reject ) => {
+					notification.on( 'show:warning', ( evt, data ) => {
+						reject( new Error( data.title ) );
+					} );
+
+					editor.document.on( 'change', () => {
+						// Check whether the image is uploaded and the image's src is replaced correctly.
+						if ( editor.getData() === '<figure class="image"><img src="http://image.mock.url/"></figure>' ) {
+							resolve();
+						}
+					} );
+
+					editor.execute( 'imageUpload', { file: createNativeFileMock() } );
+
+					setTimeout( () => {
+						upload._uploadGateway.resolveLastUpload();
+					} );
+				} );
+			} );
+		} );
+	} );
+} );

+ 4 - 0
packages/ckeditor5-easy-image/tests/manual/easyimage.html

@@ -0,0 +1,4 @@
+<div id="editor">
+	<h2>Easy Image sample</h2>
+	<p>Use the toolbar icon or paste an image file to the content.</p>
+</div>

+ 67 - 0
packages/ckeditor5-easy-image/tests/manual/easyimage.js

@@ -0,0 +1,67 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console, window, document, XMLHttpRequest */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+
+import EasyImage from '../../src/easyimage';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+
+import uid from '@ckeditor/ckeditor5-utils/src/uid';
+
+// WARNING: The URL below should not be used for any other purpose than Easy Image plugin development.
+// Images uploaded using the testing token service may be deleted automatically at any moment.
+// If you would like to try the Easy Image service, please wait until the official launch of Easy Image and sign up for a free trial.
+// Images uploaded during the free trial will not be deleted for the whole trial period and additionally the trial service can be converted
+// into a subscription at any moment, allowing you to preserve all uploaded images.
+const CLOUD_SERVICE_TOKEN_URL = 'https://demo-cs.cksource.com/testing/token';
+const CLOUD_SERVICE_UPLOAD_URL = 'https://upload-cs.cksource.com/';
+
+getToken( { id: uid() } )
+	.then( token => {
+		return ClassicEditor
+			.create( document.querySelector( '#editor' ), {
+				cloudServices: {
+					token,
+					uploadUrl: CLOUD_SERVICE_UPLOAD_URL
+				},
+				plugins: [ ArticlePluginSet, EasyImage ],
+				toolbar: [ 'headings', 'undo', 'redo', 'insertImage' ],
+				image: {
+					toolbar: [ 'imageStyleFull', 'imageStyleSide', '|', 'imageTextAlternative' ]
+				}
+			} )
+			.then( editor => {
+				window.editor = editor;
+			} );
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+
+function getToken( user ) {
+	return new Promise( ( resolve, reject ) => {
+		const xhr = new XMLHttpRequest();
+
+		xhr.open( 'GET', `${ CLOUD_SERVICE_TOKEN_URL }?user.id=${ user.id }` );
+
+		xhr.onload = () => {
+			if ( xhr.status >= 200 && xhr.status < 300 ) {
+				const response = JSON.parse( xhr.responseText );
+
+				resolve( response.token );
+			} else {
+				reject( new Error( `XHR status: ${ xhr.status }` ) );
+			}
+		};
+
+		xhr.onerror = err => {
+			reject( err );
+		};
+
+		xhr.send( null );
+	} );
+}

+ 5 - 0
packages/ckeditor5-easy-image/tests/manual/easyimage.md

@@ -0,0 +1,5 @@
+### Easy image sample
+
+1. Use the toolbar icon to past an image.
+1. Paste an image file to the content.
+1. Check whether both images upload correctly.