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

Introduced attachToform helper.

Oskar Wróbel 8 лет назад
Родитель
Сommit
9cfe026066

+ 63 - 0
packages/ckeditor5-core/src/editor/utils/attachtoform.js

@@ -0,0 +1,63 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import isFunction from '@ckeditor/ckeditor5-utils/src/lib/lodash/isFunction';
+import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+
+/**
+ * @module core/editor/utils
+ */
+
+/**
+ * Checks if editor is initialized on textarea element that belongs to a form. If yes - updates editor's element
+ * contents before submitting the form.
+ *
+ * This helper requires {@link module:core/editor/utils/elementinterface~ElementInterface ElementInterface}.
+ *
+ * @param {module:core/editor/editor} editor Editor instance.
+ */
+export default function attachToForm( editor ) {
+	if ( !isFunction( editor.updateElement ) ) {
+		/**
+		 * {@link module:core/editor/utils/elementinterface~ElementInterface ElementInterface} is required.
+		 *
+		 * @error attachtoform-missing-elementinterface
+		 */
+		throw new CKEditorError( 'attachtoform-missing-elementinterface: Element interface is required.' );
+	}
+
+	const element = editor.element;
+
+	// Only when replacing a textarea which is inside of a form element.
+	if ( element && element.tagName.toLowerCase() === 'textarea' && element.form ) {
+		let originalSubmit;
+		const form = element.form;
+		const onSubmit = () => editor.updateElement();
+
+		// Replace the original form#submit() to call a custom submit function first.
+		// Check if #submit is a function because the form might have an input named "submit".
+		if ( isFunction( form.submit ) ) {
+			originalSubmit = form.submit;
+
+			form.submit = () => {
+				onSubmit();
+				originalSubmit.apply( form );
+			};
+		}
+
+		// Update the replaced textarea with data before each form#submit event.
+		form.addEventListener( 'submit', onSubmit );
+
+		// Remove the submit listener and revert the original submit method on
+		// editor#destroy.
+		editor.on( 'destroy', () => {
+			form.removeEventListener( 'submit', onSubmit );
+
+			if ( originalSubmit ) {
+				form.submit = originalSubmit;
+			}
+		} );
+	}
+}

+ 176 - 0
packages/ckeditor5-core/tests/editor/utils/attachtoform.js

@@ -0,0 +1,176 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import attachToForm from '../../../src/editor/utils/attachtoform';
+import ElementInterface from '../../../src/editor/utils/elementinterface';
+import Editor from '../../../src/editor/editor';
+import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
+import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import mix from '@ckeditor/ckeditor5-utils/src/mix';
+
+/* global document, Event */
+
+describe( 'attachToForm()', () => {
+	let editor, form, textarea, submitStub;
+
+	beforeEach( () => {
+		form = document.createElement( 'form' );
+		textarea = document.createElement( 'textarea' );
+		form.appendChild( textarea );
+		document.body.appendChild( form );
+		submitStub = sinon.stub( form, 'submit' );
+
+		// Prevents page realods in Firefox ;|
+		form.addEventListener( 'submit', evt => {
+			evt.preventDefault();
+		} );
+
+		class CustomEditor extends Editor {}
+		mix( CustomEditor, ElementInterface );
+
+		editor = new CustomEditor();
+		editor.data.processor = new HtmlDataProcessor();
+		editor.model.document.createRoot();
+		editor.model.schema.extend( '$text', { allowIn: '$root' } );
+
+		editor.data.set( 'foo bar' );
+	} );
+
+	afterEach( () => {
+		submitStub.restore();
+		form.remove();
+
+		return editor.destroy();
+	} );
+
+	it( 'should throw an error when is used with editor without `ElementInterface`', () => {
+		expect( () => {
+			attachToForm( new Editor() );
+		} ).to.throw( CKEditorError, /^attachtoform-missing-elementinterface/ );
+	} );
+
+	it( 'should update editor#element after the "submit" event', () => {
+		editor.element = textarea;
+		attachToForm( editor );
+
+		expect( textarea.value ).to.equal( '' );
+
+		form.dispatchEvent( new Event( 'submit', {
+			// We need to be able to do preventDefault() to prevent page reloads in Firefox.
+			cancelable: true
+		} ) );
+
+		expect( textarea.value, 2 ).to.equal( 'foo bar' );
+	} );
+
+	it( 'should update editor#element after calling the submit() method', () => {
+		editor.element = textarea;
+		attachToForm( editor );
+
+		expect( textarea.value ).to.equal( '' );
+
+		// Submit method is replaced by our implementation.
+		expect( form.submit ).to.not.equal( submitStub );
+		form.submit();
+
+		expect( textarea.value ).to.equal( 'foo bar' );
+		sinon.assert.calledOnce( submitStub );
+
+		// Check if original function was called in correct context.
+		sinon.assert.calledOn( submitStub, form );
+	} );
+
+	it( 'should not update editor#element if it is not a textarea in a form', () => {
+		const element = document.createElement( 'div' );
+		form.appendChild( element );
+
+		editor.element = element;
+		attachToForm( editor );
+
+		expect( textarea.value ).to.equal( '' );
+
+		// Submit method is not replaced by our implementation.
+		expect( form.submit ).to.equal( submitStub );
+		form.submit();
+
+		expect( textarea.value ).to.equal( '' );
+	} );
+
+	it( 'should not update editor#element not belonging to a form', () => {
+		const standaloneTextarea = document.createElement( 'textarea' );
+		document.body.appendChild( standaloneTextarea );
+
+		editor.element = standaloneTextarea;
+		attachToForm( editor );
+
+		expect( standaloneTextarea.value ).to.equal( '' );
+
+		// Submit method is not replaced by our implementation.
+		expect( form.submit ).to.equal( submitStub );
+		form.submit();
+
+		expect( standaloneTextarea.value ).to.equal( '' );
+
+		standaloneTextarea.remove();
+	} );
+
+	it( 'should not update editor#element after destruction of the editor - form.submit()', () => {
+		editor.element = textarea;
+		attachToForm( editor );
+
+		expect( textarea.value ).to.equal( '' );
+
+		return editor.destroy().then( () => {
+			// Submit method is no longer replaced by our implementation.
+			expect( form.submit ).to.equal( submitStub );
+			form.submit();
+
+			expect( textarea.value ).to.equal( '' );
+		} );
+	} );
+
+	it( 'should not update the editor#element after destruction of the editor - "submit" event', () => {
+		editor.element = textarea;
+		attachToForm( editor );
+
+		expect( textarea.value ).to.equal( '' );
+
+		return editor.destroy().then( () => {
+			form.dispatchEvent( new Event( 'submit', {
+				// We need to be able to do preventDefault() to prevent page reloads in Firefox.
+				cancelable: true
+			} ) );
+
+			expect( textarea.value ).to.equal( '' );
+		} );
+	} );
+
+	it( 'should not replace submit() method when one of the elements in a form is named "submit"', () => {
+		// Restore stub since we want to mask submit function with input with name="submit".
+		submitStub.restore();
+
+		const input = document.createElement( 'input' );
+		input.setAttribute( 'name', 'submit' );
+		form.appendChild( input );
+
+		editor.element = textarea;
+		attachToForm( editor );
+
+		expect( form.submit ).to.equal( input );
+		expect( textarea.value ).to.equal( '' );
+
+		form.dispatchEvent( new Event( 'submit', {
+			// We need to be able to do preventDefault() to prevent page reloads in Firefox.
+			cancelable: true
+		} ) );
+
+		expect( textarea.value ).to.equal( 'foo bar' );
+
+		return editor.destroy().then( () => {
+			expect( form.submit ).to.equal( input );
+			input.remove();
+		} );
+	} );
+} );