Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 78719c9

Browse files
authored
Merge pull request #58 from Natim/add-strike-engine-and-plugin
Feature: Added strikethrough feature. Thanks to @Natim!
2 parents cb0c3e0 + 69e6a79 commit 78719c9

10 files changed

+344
-9
lines changed

docs/api/basic-styles.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ category: api-reference
66

77
[![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-basic-styles.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-basic-styles)
88

9-
This package contains features allowing to apply basic text formatting such as bold, italic, underline and code in CKEditor 5.
9+
This package contains features allowing to apply basic text formatting such as bold, italic, underline, strikethrough and code in CKEditor 5.
1010

1111
## Documentation
1212

1313
Check out the following plugins:
1414

1515
* {@link module:basic-styles/bold~Bold}
1616
* {@link module:basic-styles/italic~Italic}
17+
* {@link module:basic-styles/strikethrough~Strikethrough}
1718
* {@link module:basic-styles/underline~Underline}
1819
* {@link module:basic-styles/code~Code}
1920

lang/contexts.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"Bold": "Toolbar button tooltip for the Bold feature.",
33
"Italic": "Toolbar button tooltip for the Italic feature.",
44
"Underline": "Toolbar button tooltip for the Underline feature.",
5-
"Code": "Toolbar button tooltip for the Code feature."
5+
"Code": "Toolbar button tooltip for the Code feature.",
6+
"Strikethrough": "Toolbar button tooltip for the Strikethrough feature."
67
}

src/strikethrough.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @license Copyright (c) 2017, CKSource - Frederico Knabben. All rights reserved.
3+
* For licensing, see LICENSE.md.
4+
*/
5+
6+
/**
7+
* @module basic-styles/strikethrough
8+
*/
9+
10+
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11+
import StrikethroughEngine from './strikethroughengine';
12+
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
13+
import strikethroughIcon from '../theme/icons/strikethrough.svg';
14+
15+
/**
16+
* The strikethrough feature. It introduces the Strikethrough button and the <kbd>Ctrl+Shift+X</kbd> keystroke.
17+
*
18+
* It uses the {@link module:basic-styles/strikethroughengine~StrikethroughEngine strikethrough engine feature}.
19+
*
20+
* @extends module:core/plugin~Plugin
21+
*/
22+
export default class Strikethrough extends Plugin {
23+
/**
24+
* @inheritDoc
25+
*/
26+
static get requires() {
27+
return [ StrikethroughEngine ];
28+
}
29+
30+
/**
31+
* @inheritDoc
32+
*/
33+
static get pluginName() {
34+
return 'Strikethrough';
35+
}
36+
37+
/**
38+
* @inheritDoc
39+
*/
40+
init() {
41+
const editor = this.editor;
42+
const t = editor.t;
43+
const command = editor.commands.get( 'strikethrough' );
44+
const keystroke = 'CTRL+SHIFT+X';
45+
46+
// Add strikethrough button to feature components.
47+
editor.ui.componentFactory.add( 'strikethrough', locale => {
48+
const view = new ButtonView( locale );
49+
50+
view.set( {
51+
label: t( 'Strikethrough' ),
52+
icon: strikethroughIcon,
53+
keystroke,
54+
tooltip: true
55+
} );
56+
57+
view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
58+
59+
// Execute command.
60+
this.listenTo( view, 'execute', () => editor.execute( 'strikethrough' ) );
61+
62+
return view;
63+
} );
64+
65+
// Set the Ctrl+Shift+X keystroke.
66+
editor.keystrokes.set( keystroke, 'strikethrough' );
67+
}
68+
}

src/strikethroughengine.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @license Copyright (c) 2017, CKSource - Frederico Knabben. All rights reserved.
3+
* For licensing, see LICENSE.md.
4+
*/
5+
6+
/**
7+
* @module basic-styles/strikeengine
8+
*/
9+
10+
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11+
import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
12+
import buildViewConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildviewconverter';
13+
import AttributeCommand from './attributecommand';
14+
15+
const STRIKETHROUGH = 'strikethrough';
16+
17+
/**
18+
* The strikethrough engine feature.
19+
*
20+
* It registers the `strikethrough` command and introduces the
21+
* `strikethroughsthrough` attribute in the model which renders to the view
22+
* as a `<s>` element.
23+
*
24+
* @extends module:core/plugin~Plugin
25+
*/
26+
export default class StrikethroughEngine extends Plugin {
27+
/**
28+
* @inheritDoc
29+
*/
30+
init() {
31+
const editor = this.editor;
32+
const data = editor.data;
33+
const editing = editor.editing;
34+
35+
// Allow strikethrough attribute on all inline nodes.
36+
editor.document.schema.allow( { name: '$inline', attributes: STRIKETHROUGH, inside: '$block' } );
37+
// Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477.
38+
editor.document.schema.allow( { name: '$inline', attributes: STRIKETHROUGH, inside: '$clipboardHolder' } );
39+
40+
// Build converter from model to view for data and editing pipelines.
41+
buildModelConverter().for( data.modelToView, editing.modelToView )
42+
.fromAttribute( STRIKETHROUGH )
43+
.toElement( 's' );
44+
45+
// Build converter from view to model for data pipeline.
46+
buildViewConverter().for( data.viewToModel )
47+
.fromElement( 's' )
48+
.fromElement( 'del' )
49+
.fromElement( 'strike' )
50+
.fromAttribute( 'style', { 'text-decoration': 'line-through' } )
51+
.toAttribute( STRIKETHROUGH, true );
52+
53+
// Create strikethrough command.
54+
editor.commands.add( STRIKETHROUGH, new AttributeCommand( editor, STRIKETHROUGH ) );
55+
}
56+
}

tests/manual/basic-styles.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
<div id="editor">
2-
<p><i>This</i> <code>is an</code> <strong>editor</strong> <u>instance</u>.</p>
2+
<p><i>This</i> <s>is</s> <code>an</code> <strong>editor</strong> <u>instance</u>.</p>
33
</div>

tests/manual/basic-styles.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@ import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
1010
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
1111
import Bold from '../../src/bold';
1212
import Italic from '../../src/italic';
13+
import Strikethrough from '../../src/strikethrough';
1314
import Underline from '../../src/underline';
1415
import Code from '../../src/code';
1516

1617
ClassicEditor
1718
.create( document.querySelector( '#editor' ), {
18-
plugins: [ Essentials, Paragraph, Bold, Italic, Underline, Code ],
19-
toolbar: [ 'bold', 'italic', 'underline', 'code', 'undo', 'redo' ]
19+
plugins: [ Essentials, Paragraph, Bold, Italic, Strikethrough, Underline, Code ],
20+
toolbar: [ 'bold', 'italic', 'strikethrough', 'underline', 'code', 'undo', 'redo' ]
2021
} )
2122
.then( editor => {
2223
window.editor = editor;

tests/manual/basic-styles.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
## Basic styles
22

33
1. The data should be loaded with:
4-
* italic "This",
5-
* bold "editor",
4+
* italic _"This"_,
5+
* bold **"editor"**,
66
* underline "instance",
7-
* code "is an".
8-
2. Test the bold, italic, underline and code features live.
7+
* strikethrough ~~"is"~~,
8+
* code `"an"`.
9+
2. Test the bold, italic, strikethrough, underline and code features live.

tests/strikethrough.js

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/**
2+
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3+
* For licensing, see LICENSE.md.
4+
*/
5+
6+
/* globals document */
7+
8+
import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
9+
import Strikethrough from '../src/strikethrough';
10+
import StrikethroughEngine from '../src/strikethroughengine';
11+
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
12+
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
13+
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
14+
15+
testUtils.createSinonSandbox();
16+
17+
describe( 'Strikethrough', () => {
18+
let editor, strikeView;
19+
20+
beforeEach( () => {
21+
const editorElement = document.createElement( 'div' );
22+
document.body.appendChild( editorElement );
23+
24+
return ClassicTestEditor
25+
.create( editorElement, {
26+
plugins: [ Strikethrough ]
27+
} )
28+
.then( newEditor => {
29+
editor = newEditor;
30+
31+
strikeView = editor.ui.componentFactory.create( 'strikethrough' );
32+
} );
33+
} );
34+
35+
afterEach( () => {
36+
return editor.destroy();
37+
} );
38+
39+
it( 'should be loaded', () => {
40+
expect( editor.plugins.get( Strikethrough ) ).to.be.instanceOf( Strikethrough );
41+
} );
42+
43+
it( 'should load StrikethroughEngine', () => {
44+
expect( editor.plugins.get( StrikethroughEngine ) ).to.be.instanceOf( StrikethroughEngine );
45+
} );
46+
47+
it( 'should register strikethrough feature component', () => {
48+
expect( strikeView ).to.be.instanceOf( ButtonView );
49+
expect( strikeView.isOn ).to.be.false;
50+
expect( strikeView.label ).to.equal( 'Strikethrough' );
51+
expect( strikeView.icon ).to.match( /<svg / );
52+
expect( strikeView.keystroke ).to.equal( 'CTRL+SHIFT+X' );
53+
} );
54+
55+
it( 'should execute strikethrough command on model execute event', () => {
56+
const executeSpy = testUtils.sinon.spy( editor, 'execute' );
57+
58+
strikeView.fire( 'execute' );
59+
60+
sinon.assert.calledOnce( executeSpy );
61+
sinon.assert.calledWithExactly( executeSpy, 'strikethrough' );
62+
} );
63+
64+
it( 'should bind model to strikethrough command', () => {
65+
const command = editor.commands.get( 'strikethrough' );
66+
67+
expect( strikeView.isOn ).to.be.false;
68+
69+
expect( strikeView.isEnabled ).to.be.false;
70+
71+
command.value = true;
72+
expect( strikeView.isOn ).to.be.true;
73+
74+
command.isEnabled = true;
75+
expect( strikeView.isEnabled ).to.be.true;
76+
} );
77+
78+
it( 'should set keystroke in the model', () => {
79+
expect( strikeView.keystroke ).to.equal( 'CTRL+SHIFT+X' );
80+
} );
81+
82+
it( 'should set editor keystroke', () => {
83+
const spy = sinon.spy( editor, 'execute' );
84+
const keyEventData = {
85+
keyCode: keyCodes.x,
86+
shiftKey: true,
87+
ctrlKey: true,
88+
preventDefault: sinon.spy(),
89+
stopPropagation: sinon.spy()
90+
};
91+
92+
const wasHandled = editor.keystrokes.press( keyEventData );
93+
94+
expect( wasHandled ).to.be.true;
95+
expect( spy.calledOnce ).to.be.true;
96+
expect( keyEventData.preventDefault.calledOnce ).to.be.true;
97+
} );
98+
} );

tests/strikethroughengine.js

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3+
* For licensing, see LICENSE.md.
4+
*/
5+
6+
import StrikethroughEngine from '../src/strikethroughengine';
7+
8+
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
9+
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
10+
import AttributeCommand from '../src/attributecommand';
11+
12+
import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
13+
import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
14+
15+
describe( 'StrikethroughEngine', () => {
16+
let editor, doc;
17+
18+
beforeEach( () => {
19+
return VirtualTestEditor
20+
.create( {
21+
plugins: [ Paragraph, StrikethroughEngine ]
22+
} )
23+
.then( newEditor => {
24+
editor = newEditor;
25+
26+
doc = editor.document;
27+
} );
28+
} );
29+
30+
afterEach( () => {
31+
return editor.destroy();
32+
} );
33+
34+
it( 'should be loaded', () => {
35+
expect( editor.plugins.get( StrikethroughEngine ) ).to.be.instanceOf( StrikethroughEngine );
36+
} );
37+
38+
it( 'should set proper schema rules', () => {
39+
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$root' } ) ).to.be.false;
40+
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$block' } ) ).to.be.true;
41+
expect( doc.schema.check( { name: '$inline', attributes: 'strikethrough', inside: '$clipboardHolder' } ) ).to.be.true;
42+
} );
43+
44+
describe( 'command', () => {
45+
it( 'should register strikethrough command', () => {
46+
const command = editor.commands.get( 'strikethrough' );
47+
48+
expect( command ).to.be.instanceOf( AttributeCommand );
49+
expect( command ).to.have.property( 'attributeKey', 'strikethrough' );
50+
} );
51+
} );
52+
53+
describe( 'data pipeline conversions', () => {
54+
it( 'should convert <strike> to strikethrough attribute', () => {
55+
editor.setData( '<p><strike>foo</strike>bar</p>' );
56+
57+
expect( getModelData( doc, { withoutSelection: true } ) )
58+
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );
59+
60+
expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
61+
} );
62+
it( 'should convert <del> to strikethrough attribute', () => {
63+
editor.setData( '<p><del>foo</del>bar</p>' );
64+
65+
expect( getModelData( doc, { withoutSelection: true } ) )
66+
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );
67+
68+
expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
69+
} );
70+
71+
it( 'should convert <s> to strikethrough attribute', () => {
72+
editor.setData( '<p><s>foo</s>bar</p>' );
73+
74+
expect( getModelData( doc, { withoutSelection: true } ) )
75+
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );
76+
77+
expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
78+
} );
79+
80+
it( 'should convert text-decoration:line-through to strikethrough attribute', () => {
81+
editor.setData( '<p><span style="text-decoration: line-through;">foo</span>bar</p>' );
82+
83+
expect( getModelData( doc, { withoutSelection: true } ) )
84+
.to.equal( '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );
85+
86+
expect( editor.getData() ).to.equal( '<p><s>foo</s>bar</p>' );
87+
} );
88+
89+
it( 'should be integrated with autoparagraphing', () => {
90+
// Incorrect results because autoparagraphing works incorrectly (issue in paragraph).
91+
// https://github.com/ckeditor/ckeditor5-paragraph/issues/10
92+
93+
editor.setData( '<s>foo</s>bar' );
94+
95+
expect( getModelData( doc, { withoutSelection: true } ) ).to.equal( '<paragraph>foobar</paragraph>' );
96+
97+
expect( editor.getData() ).to.equal( '<p>foobar</p>' );
98+
} );
99+
} );
100+
101+
describe( 'editing pipeline conversion', () => {
102+
it( 'should convert attribute', () => {
103+
setModelData( doc, '<paragraph><$text strikethrough="true">foo</$text>bar</paragraph>' );
104+
105+
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><s>foo</s>bar</p>' );
106+
} );
107+
} );
108+
} );

theme/icons/strikethrough.svg

+1
Loading

0 commit comments

Comments
 (0)