8
0
Pārlūkot izejas kodu

add subscript and superscript feature

idleb 7 gadi atpakaļ
vecāks
revīzija
7366cb5792

+ 36 - 0
packages/ckeditor5-basic-styles/src/subscript.js

@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SubscriptEditing from './subscript/subscriptediting';
+import SubscriptUI from './subscript/subscriptui';
+
+/**
+ * The subscript feature.
+ *
+ * It loads the {@link module:basic-styles/subscript/subscriptediting~SubscriptEditing} and
+ * {@link module:basic-styles/subscript/subscriptui~SubscriptUI} plugins.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Subscript extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SubscriptEditing, SubscriptUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'Sub';
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/subscript/subscriptediting.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript/subscriptediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import AttributeCommand from '../attributecommand';
+
+const SUB = 'sub';
+
+/**
+ * The subscript editing feature.
+ *
+ * It registers the `sub` command and introduces the `sub` attribute in the model which renders to the view
+ * as a `<sub>` element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SubscriptEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		// Allow sub attribute on text nodes.
+		editor.model.schema.extend( '$text', { allowAttributes: SUB } );
+
+		// Build converter from model to view for data and editing pipelines.
+
+		editor.conversion.attributeToElement( {
+			model: SUB,
+			view: 'sub',
+			upcastAlso: [
+				{
+					styles: {
+						'vertical-align': 'sub'
+					}
+				}
+			]
+		} );
+
+		// Create sub command.
+		editor.commands.add( SUB, new AttributeCommand( editor, SUB ) );
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/subscript/subscriptui.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/subscript/subscriptui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import subscriptIcon from '../../theme/icons/subscript.svg';
+
+const SUB = 'sub';
+
+/**
+ * The subscript UI feature. It introduces the Subscript button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SubscriptUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		// Add subscript button to feature components.
+		editor.ui.componentFactory.add( SUB, locale => {
+			const command = editor.commands.get( SUB );
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: t( 'Subscript' ),
+				icon: subscriptIcon,
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( SUB ) );
+
+			return view;
+		} );
+	}
+}

+ 36 - 0
packages/ckeditor5-basic-styles/src/superscript.js

@@ -0,0 +1,36 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import SuperscriptEditing from './superscript/superscriptediting';
+import SuperscriptUI from './superscript/superscriptui';
+
+/**
+ * The superscript feature.
+ *
+ * It loads the {@link module:basic-styles/superscript/superscriptediting~SuperscriptEditing} and
+ * {@link module:basic-styles/superscript/superscriptui~SuperscriptUI} plugins.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Superscript extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ SuperscriptEditing, SuperscriptUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'Super';
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/superscript/superscriptediting.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript/superscriptediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import AttributeCommand from '../attributecommand';
+
+const SUPER = 'super';
+
+/**
+ * The superscript editing feature.
+ *
+ * It registers the `super` command and introduces the `super` attribute in the model which renders to the view
+ * as a `<super>` element.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SuperscriptEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		// Allow super attribute on text nodes.
+		editor.model.schema.extend( '$text', { allowAttributes: SUPER } );
+
+		// Build converter from model to view for data and editing pipelines.
+
+		editor.conversion.attributeToElement( {
+			model: SUPER,
+			view: 'sup',
+			upcastAlso: [
+				{
+					styles: {
+						'vertical-align': 'super'
+					}
+				}
+			]
+		} );
+
+		// Create super command.
+		editor.commands.add( SUPER, new AttributeCommand( editor, SUPER ) );
+	}
+}

+ 49 - 0
packages/ckeditor5-basic-styles/src/superscript/superscriptui.js

@@ -0,0 +1,49 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module basic-styles/superscript/superscriptui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+
+import superscriptIcon from '../../theme/icons/superscript.svg';
+
+const SUPER = 'super';
+
+/**
+ * The superscript UI feature. It introduces the Superscript button.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class SuperscriptUI extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+
+		// Add superscript button to feature components.
+		editor.ui.componentFactory.add( SUPER, locale => {
+			const command = editor.commands.get( SUPER );
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: t( 'Superscript' ),
+				icon: superscriptIcon,
+				tooltip: true
+			} );
+
+			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( SUPER ) );
+
+			return view;
+		} );
+	}
+}

+ 0 - 0
packages/ckeditor5-basic-styles/tests/subscript.js


+ 87 - 0
packages/ckeditor5-basic-styles/tests/subscript/subscriptediting.js

@@ -0,0 +1,87 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import SubEditing from '../../src/subscript/subscriptediting';
+
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import AttributeCommand from '../../src/attributecommand';
+
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+
+describe( 'SubEditing', () => {
+	let editor, model;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ Paragraph, SubEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should be loaded', () => {
+		expect( editor.plugins.get( SubEditing ) ).to.be.instanceOf( SubEditing );
+	} );
+
+	it( 'should set proper schema rules', () => {
+		expect( model.schema.checkAttribute( [ '$root', '$block', '$text' ], 'sub' ) ).to.be.true;
+		expect( model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'sub' ) ).to.be.true;
+	} );
+
+	describe( 'command', () => {
+		it( 'should register sub command', () => {
+			const command = editor.commands.get( 'sub' );
+
+			expect( command ).to.be.instanceOf( AttributeCommand );
+			expect( command ).to.have.property( 'attributeKey', 'sub' );
+		} );
+	} );
+
+	describe( 'data pipeline conversions', () => {
+		it( 'should convert <sub> to sub attribute', () => {
+			editor.setData( '<p><sub>foo</sub>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sub="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+
+		it( 'should convert vertical-align:sub to sub attribute', () => {
+			editor.setData( '<p><span style="vertical-align: sub;">foo</span>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sub="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+
+		it( 'should be integrated with autoparagraphing', () => {
+			editor.setData( '<sub>foo</sub>bar' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sub="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+	} );
+
+	describe( 'editing pipeline conversion', () => {
+		it( 'should convert attribute', () => {
+			setModelData( model, '<paragraph><$text sub="true">foo</$text>bar</paragraph>' );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><sub>foo</sub>bar</p>' );
+		} );
+	} );
+} );

+ 68 - 0
packages/ckeditor5-basic-styles/tests/subscript/subscriptui.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import SubscriptEditing from '../../src/subscript/subscriptediting';
+import SubscriptUI from '../../src/subscript/subscriptui';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
+describe( 'SubscriptUI', () => {
+	let editor, subView;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ Paragraph, SubscriptEditing, SubscriptUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+
+				subView = editor.ui.componentFactory.create( 'sub' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should register subscript feature component', () => {
+		expect( subView ).to.be.instanceOf( ButtonView );
+		expect( subView.isOn ).to.be.false;
+		expect( subView.label ).to.equal( 'Subscript' );
+		expect( subView.icon ).to.match( /<svg / );
+	} );
+
+	it( 'should execute sub command on model execute event', () => {
+		const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+
+		subView.fire( 'execute' );
+
+		sinon.assert.calledOnce( executeSpy );
+		sinon.assert.calledWithExactly( executeSpy, 'sub' );
+	} );
+
+	it( 'should bind model to sub command', () => {
+		const command = editor.commands.get( 'sub' );
+
+		expect( subView.isOn ).to.be.false;
+		expect( subView.isEnabled ).to.be.true;
+
+		command.value = true;
+		expect( subView.isOn ).to.be.true;
+
+		command.isEnabled = false;
+		expect( subView.isEnabled ).to.be.false;
+	} );
+} );

+ 18 - 0
packages/ckeditor5-basic-styles/tests/superscript.js

@@ -0,0 +1,18 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Superscript from '../src/superscript';
+import SuperEditing from '../src/superscript/superscriptediting';
+import SuperUI from '../src/superscript/superscriptui';
+
+describe( 'Superscript', () => {
+	it( 'should require SuperEditing and SuperUI', () => {
+		expect( Superscript.requires ).to.deep.equal( [ SuperEditing, SuperUI ] );
+	} );
+
+	it( 'should be named', () => {
+		expect( Superscript.pluginName ).to.equal( 'Super' );
+	} );
+} );

+ 87 - 0
packages/ckeditor5-basic-styles/tests/superscript/superscriptediting.js

@@ -0,0 +1,87 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import SuperEditing from '../../src/superscript/superscriptediting';
+
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import AttributeCommand from '../../src/attributecommand';
+
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
+
+describe( 'SuperEditing', () => {
+	let editor, model;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ Paragraph, SuperEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should be loaded', () => {
+		expect( editor.plugins.get( SuperEditing ) ).to.be.instanceOf( SuperEditing );
+	} );
+
+	it( 'should set proper schema rules', () => {
+		expect( model.schema.checkAttribute( [ '$root', '$block', '$text' ], 'sup' ) ).to.be.true;
+		expect( model.schema.checkAttribute( [ '$clipboardHolder', '$text' ], 'sup' ) ).to.be.true;
+	} );
+
+	describe( 'command', () => {
+		it( 'should register super command', () => {
+			const command = editor.commands.get( 'super' );
+
+			expect( command ).to.be.instanceOf( AttributeCommand );
+			expect( command ).to.have.property( 'attributeKey', 'super' );
+		} );
+	} );
+
+	describe( 'data pipeline conversions', () => {
+		it( 'should convert <sup> to sup attribute', () => {
+			editor.setData( '<p><sup>foo</sup>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sup="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+
+		it( 'should convert vertical-align:super to sup attribute', () => {
+			editor.setData( '<p><span style="vertical-align: sup;">foo</span>bar</p>' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sup="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+
+		it( 'should be integrated with autoparagraphing', () => {
+			editor.setData( '<sup>foo</sup>bar' );
+
+			expect( getModelData( model, { withoutSelection: true } ) )
+				.to.equal( '<paragraph><$text sup="true">foo</$text>bar</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+	} );
+
+	describe( 'editing pipeline conversion', () => {
+		it( 'should convert attribute', () => {
+			setModelData( model, '<paragraph><$text sup="true">foo</$text>bar</paragraph>' );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><sup>foo</sup>bar</p>' );
+		} );
+	} );
+} );

+ 68 - 0
packages/ckeditor5-basic-styles/tests/superscript/superscriptui.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals document */
+
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
+import SuperscriptEditing from '../../src/superscript/superscriptediting';
+import SuperscriptUI from '../../src/superscript/superscriptui';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+
+describe( 'SuperscriptUI', () => {
+	let editor, superView;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		const editorElement = document.createElement( 'div' );
+		document.body.appendChild( editorElement );
+
+		return ClassicTestEditor
+			.create( editorElement, {
+				plugins: [ Paragraph, SuperscriptEditing, SuperscriptUI ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+
+				superView = editor.ui.componentFactory.create( 'super' );
+			} );
+	} );
+
+	afterEach( () => {
+		return editor.destroy();
+	} );
+
+	it( 'should register superscript feature component', () => {
+		expect( superView ).to.be.instanceOf( ButtonView );
+		expect( superView.isOn ).to.be.false;
+		expect( superView.label ).to.equal( 'Superscript' );
+		expect( superView.icon ).to.match( /<svg / );
+	} );
+
+	it( 'should execute super command on model execute event', () => {
+		const executeSpy = testUtils.sinon.spy( editor, 'execute' );
+
+		superView.fire( 'execute' );
+
+		sinon.assert.calledOnce( executeSpy );
+		sinon.assert.calledWithExactly( executeSpy, 'super' );
+	} );
+
+	it( 'should bind model to super command', () => {
+		const command = editor.commands.get( 'super' );
+
+		expect( superView.isOn ).to.be.false;
+		expect( superView.isEnabled ).to.be.true;
+
+		command.value = true;
+		expect( superView.isOn ).to.be.true;
+
+		command.isEnabled = false;
+		expect( superView.isEnabled ).to.be.false;
+	} );
+} );

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 10 - 0
packages/ckeditor5-basic-styles/theme/icons/subscript.svg


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 10 - 0
packages/ckeditor5-basic-styles/theme/icons/superscript.svg