1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | import { Plugin } from 'ckeditor5/src/core';
|
11 | import { Delete } from 'ckeditor5/src/typing';
|
12 |
|
13 | import blockAutoformatEditing from './blockautoformatediting';
|
14 | import inlineAutoformatEditing from './inlineautoformatediting';
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | export default class Autoformat extends Plugin {
|
25 | |
26 |
|
27 |
|
28 | static get requires() {
|
29 | return [ Delete ];
|
30 | }
|
31 |
|
32 | |
33 |
|
34 |
|
35 | static get pluginName() {
|
36 | return 'Autoformat';
|
37 | }
|
38 |
|
39 | |
40 |
|
41 |
|
42 | afterInit() {
|
43 | this._addListAutoformats();
|
44 | this._addBasicStylesAutoformats();
|
45 | this._addHeadingAutoformats();
|
46 | this._addBlockQuoteAutoformats();
|
47 | this._addCodeBlockAutoformats();
|
48 | this._addHorizontalLineAutoformats();
|
49 | }
|
50 |
|
51 | |
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | _addListAutoformats() {
|
63 | const commands = this.editor.commands;
|
64 |
|
65 | if ( commands.get( 'bulletedList' ) ) {
|
66 | blockAutoformatEditing( this.editor, this, /^[*-]\s$/, 'bulletedList' );
|
67 | }
|
68 |
|
69 | if ( commands.get( 'numberedList' ) ) {
|
70 | blockAutoformatEditing( this.editor, this, /^1[.|)]\s$/, 'numberedList' );
|
71 | }
|
72 |
|
73 | if ( commands.get( 'todoList' ) ) {
|
74 | blockAutoformatEditing( this.editor, this, /^\[\s?\]\s$/, 'todoList' );
|
75 | }
|
76 |
|
77 | if ( commands.get( 'checkTodoList' ) ) {
|
78 | blockAutoformatEditing( this.editor, this, /^\[\s?x\s?\]\s$/, () => {
|
79 | this.editor.execute( 'todoList' );
|
80 | this.editor.execute( 'checkTodoList' );
|
81 | } );
|
82 | }
|
83 | }
|
84 |
|
85 | |
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | _addBasicStylesAutoformats() {
|
101 | const commands = this.editor.commands;
|
102 |
|
103 | if ( commands.get( 'bold' ) ) {
|
104 | const boldCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'bold' );
|
105 |
|
106 | inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*\*)([^*]+)(\*\*)$/g, boldCallback );
|
107 | inlineAutoformatEditing( this.editor, this, /(?:^|\s)(__)([^_]+)(__)$/g, boldCallback );
|
108 | }
|
109 |
|
110 | if ( commands.get( 'italic' ) ) {
|
111 | const italicCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'italic' );
|
112 |
|
113 |
|
114 |
|
115 | inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*)([^*_]+)(\*)$/g, italicCallback );
|
116 | inlineAutoformatEditing( this.editor, this, /(?:^|\s)(_)([^_]+)(_)$/g, italicCallback );
|
117 | }
|
118 |
|
119 | if ( commands.get( 'code' ) ) {
|
120 | const codeCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'code' );
|
121 |
|
122 | inlineAutoformatEditing( this.editor, this, /(`)([^`]+)(`)$/g, codeCallback );
|
123 | }
|
124 |
|
125 | if ( commands.get( 'strikethrough' ) ) {
|
126 | const strikethroughCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'strikethrough' );
|
127 |
|
128 | inlineAutoformatEditing( this.editor, this, /(~~)([^~]+)(~~)$/g, strikethroughCallback );
|
129 | }
|
130 | }
|
131 |
|
132 | /**
|
133 | * Adds autoformatting related to {@link module:heading/heading~Heading}.
|
134 | *
|
135 | * It is using a number at the end of the command name to associate it with the proper trigger:
|
136 | *
|
137 | * * `heading` with value `heading1` will be executed when typing `#`,
|
138 | * * `heading` with value `heading2` will be executed when typing `##`,
|
139 | * * ... up to `heading6` and `######`.
|
140 | *
|
141 | * @private
|
142 | */
|
143 | _addHeadingAutoformats() {
|
144 | const command = this.editor.commands.get( 'heading' );
|
145 |
|
146 | if ( command ) {
|
147 | command.modelElements
|
148 | .filter( name => name.match( /^heading[1-6]$/ ) )
|
149 | .forEach( modelName => {
|
150 | const level = modelName[ 7 ];
|
151 | const pattern = new RegExp( `^(#{${ level }})\\s$` );
|
152 |
|
153 | blockAutoformatEditing( this.editor, this, pattern, () => {
|
154 |
|
155 | if ( !command.isEnabled || command.value === modelName ) {
|
156 | return false;
|
157 | }
|
158 |
|
159 | this.editor.execute( 'heading', { value: modelName } );
|
160 | } );
|
161 | } );
|
162 | }
|
163 | }
|
164 |
|
165 | |
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 | _addBlockQuoteAutoformats() {
|
174 | if ( this.editor.commands.get( 'blockQuote' ) ) {
|
175 | blockAutoformatEditing( this.editor, this, /^>\s$/, 'blockQuote' );
|
176 | }
|
177 | }
|
178 |
|
179 | |
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | _addCodeBlockAutoformats() {
|
188 | const editor = this.editor;
|
189 | const selection = editor.model.document.selection;
|
190 |
|
191 | if ( editor.commands.get( 'codeBlock' ) ) {
|
192 | blockAutoformatEditing( editor, this, /^```$/, () => {
|
193 | if ( selection.getFirstPosition().parent.is( 'element', 'listItem' ) ) {
|
194 | return false;
|
195 | }
|
196 | this.editor.execute( 'codeBlock', {
|
197 | usePreviousLanguageChoice: true
|
198 | } );
|
199 | } );
|
200 | }
|
201 | }
|
202 |
|
203 | /**
|
204 | * Adds autoformatting related to {@link module:horizontal-line/horizontalline~HorizontalLine}.
|
205 | *
|
206 | * When typed:
|
207 | * - `` --- `` – Will be replaced with a horizontal line.
|
208 | *
|
209 | * @private
|
210 | */
|
211 | _addHorizontalLineAutoformats() {
|
212 | if ( this.editor.commands.get( 'horizontalLine' ) ) {
|
213 | blockAutoformatEditing( this.editor, this, /^---$/, 'horizontalLine' );
|
214 | }
|
215 | }
|
216 | }
|
217 |
|
218 | // Helper function for getting `inlineAutoformatEditing` callbacks that checks if command is enabled.
|
219 | //
|
220 | // @param {module:core/editor/editor~Editor} editor
|
221 | // @param {String} attributeKey
|
222 | // @returns {Function}
|
223 | function getCallbackFunctionForInlineAutoformat( editor, attributeKey ) {
|
224 | return ( writer, rangesToFormat ) => {
|
225 | const command = editor.commands.get( attributeKey );
|
226 |
|
227 | if ( !command.isEnabled ) {
|
228 | return false;
|
229 | }
|
230 |
|
231 | const validRanges = editor.model.schema.getValidRanges( rangesToFormat, attributeKey );
|
232 |
|
233 | for ( const range of validRanges ) {
|
234 | writer.setAttribute( attributeKey, true, range );
|
235 | }
|
236 |
|
237 | // After applying attribute to the text, remove given attribute from the selection.
|
238 | // This way user is able to type a text without attribute used by auto formatter.
|
239 | writer.removeSelectionAttribute( attributeKey );
|
240 | };
|
241 | }
|
242 |
|
\ | No newline at end of file |