Browse Source

WIP - initial implementation of autosave feature.

Maciej Bukowski 7 years ago
parent
commit
e9e5803e69

+ 3 - 0
packages/ckeditor5-autosave/package.json

@@ -9,8 +9,11 @@
     "ckeditor5-feature"
   ],
   "dependencies": {
+	"@ckeditor/ckeditor5-utils": "^10.0.0",
+	"@ckeditor/ckeditor5-core": "^10.0.0"
   },
   "devDependencies": {
+	"@ckeditor/ckeditor5-engine": "^10.0.0",
     "eslint": "^4.15.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",

+ 119 - 0
packages/ckeditor5-autosave/src/autosave.js

@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2016 - 2017, CKSource - Frederico Knabben. All rights reserved.
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import PendingActions from '@ckeditor/ckeditor5-core/src/pendingactions';
+import throttle from '@ckeditor/ckeditor5-utils/src/lib/lodash/throttle';
+import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
+
+/* globals window */
+
+/**
+ * TODO: ...
+ * Autosave plugin provides an easy-to-use API for getting sync with the editor's content.
+ * It watches `Document:change`, and `Window:beforeunload` events.
+ * At these moments, editor.getBody() and editor.getTitle() should work.
+ */
+export default class Autosave extends Plugin {
+	static get pluginName() {
+		return 'Autosave';
+	}
+
+	static get requires() {
+		return [ PendingActions ];
+	}
+
+	constructor( editor ) {
+		super( editor );
+
+		/**
+		 * @public
+		 * @member {Provider}
+		 */
+		this.provider = undefined;
+
+		/**
+		 * @private
+		 * @type {Function}
+		 */
+		this._throttledSave = throttle( this._save.bind( this ), 500 );
+
+		/**
+		 * @private
+		 * @type {DomEmitterMixin}
+		 */
+		this._domEmitter = Object.create( DomEmitterMixin );
+
+		/**
+		 * @protected
+		 * @type {Number}
+		 */
+		this._lastDocumentVersion = editor.model.document.version;
+	}
+
+	init() {
+		const editor = this.editor;
+		const doc = editor.model.document;
+		const pendingActions = editor.plugins.get( PendingActions );
+
+		this.listenTo( doc, 'change', this._throttledSave );
+
+		// TODO: Docs
+		// Flush on the editor's destroy listener with the highest priority to ensure that
+		// `editor.getBody` will be called before plugins are destroyed.
+		this.listenTo( editor, 'destroy', () => this._save(), { priority: 'highest' } );
+
+		// It's not possible to easy test it because karma uses `beforeunload` event
+		// to warn before full page reload and this event cannot be dispatched manually.
+		/* istanbul ignore next */
+		this._domEmitter.listenTo( window, 'beforeunload', ( evtInfo, domEvt ) => {
+			if ( pendingActions.isPending ) {
+				domEvt.returnValue = pendingActions.first.message;
+			}
+		} );
+	}
+
+	destroy() {
+		this._throttledSave.cancel();
+		this._domEmitter.stopListening();
+		super.destroy();
+	}
+
+	/**
+	 * TODO: Docs.
+	 */
+	save() {
+		this._throttledSave.cancel();
+
+		const version = this.editor.model.document.version;
+
+		if ( version <= this._lastDocumentVersion ) {
+			return Promise.resolve();
+		}
+
+		this._lastDocumentVersion = version;
+
+		if ( !this.provider ) {
+			return Promise.resolve();
+		}
+
+		// TODO: add pending action.
+
+		return Promise.resolve()
+			.then( () => this.provider.save() )
+			.then( () => {
+				// TODO: remove pending action.
+			} );
+	}
+}
+
+/**
+ * @typedef Provider
+ */
+
+/**
+ * @function
+ * @name Provider#save
+ * @type {Function}
+ */

+ 46 - 0
packages/ckeditor5-autosave/tests/autosave.js

@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2016 - 2017, CKSource - Frederico Knabben. All rights reserved.
+ */
+
+/* globals document */
+
+// import ModelText from '@ckeditor/ckeditor5-engine/src/model/text';
+// import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import Autosave from '../src/autosave';
+
+describe( 'Autosave', () => {
+	const sandbox = sinon.sandbox.create();
+	let editor, element, autosave;
+
+	beforeEach( () => {
+		element = document.createElement( 'div' );
+		document.body.appendChild( element );
+
+		return ClassicTestEditor
+			.create( element, {
+				plugins: []
+			} )
+			.then( _editor => {
+				editor = _editor;
+				autosave = editor.plugins.get( Autosave );
+			} );
+	} );
+
+	afterEach( () => {
+		document.body.removeChild( element );
+		sandbox.restore();
+
+		return editor.destroy();
+	} );
+
+	it( 'should have static pluginName property', () => {
+		expect( Autosave.pluginName ).to.equal( 'Autosave' );
+	} );
+
+	describe( 'initialization', () => {
+		it( 'should initialize provider with an undefined value', () => {
+			expect( autosave.provider ).to.be.undefined;
+		} );
+	} );
+} );