@alitons/ckeditor5 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,603 @@
1
+ import { Plugin, Command } from '@ckeditor/ckeditor5-core';
2
+ import { findOptimalInsertionRange } from '@ckeditor/ckeditor5-widget/src/utils';
3
+ import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
4
+
5
+ export default class NumberedDivList extends Plugin {
6
+ static get pluginName() {
7
+ return 'NumberedDivList';
8
+ }
9
+
10
+ init() {
11
+ const editor = this.editor;
12
+ const toolbar = editor.config.get('toolbar') as any;
13
+ const config = toolbar!.listaNumeradaOptions || {};
14
+
15
+ editor.model.schema.register('numList', {
16
+ allowWhere: '$block',
17
+ allowContentOf: '$root',
18
+ allowAttributesOf: '$block',
19
+ isBlock: true,
20
+ isLimit: true,
21
+ });
22
+
23
+ editor.model.schema.register('numItem', {
24
+ allowIn: 'numList',
25
+ allowContentOf: '$root',
26
+ allowAttributesOf: '$block',
27
+ isLimit: true,
28
+ });
29
+
30
+ const downcastAttr = (conv: any, key: 'dataStyle'|'dataBlock', viewKey: 'data-style'|'data-block') => {
31
+ conv.attributeToAttribute({
32
+ model: { name: 'numList', key },
33
+ view: (val: unknown) => {
34
+ if (val == null || val === '') return { key: viewKey, value: null };
35
+ return { key: viewKey, value: String(val) };
36
+ }
37
+ });
38
+ };
39
+ downcastAttr(editor.conversion.for('editingDowncast'), 'dataStyle', 'data-style');
40
+ downcastAttr(editor.conversion.for('dataDowncast'), 'dataStyle', 'data-style');
41
+ downcastAttr(editor.conversion.for('editingDowncast'), 'dataBlock', 'data-block');
42
+ downcastAttr(editor.conversion.for('dataDowncast'), 'dataBlock', 'data-block');
43
+
44
+ editor.conversion.for('dataDowncast').elementToElement({
45
+ model: 'numList',
46
+ view: (modelElement, { writer }) => {
47
+ const attrs: Record<string, string> = { class: 'num-list' };
48
+
49
+ const ds = modelElement.getAttribute('data-style');
50
+ if (ds != null) attrs['data-style'] = String(ds);
51
+
52
+ const db = modelElement.getAttribute('data-block');
53
+ if (db != null) attrs['data-block'] = String(db);
54
+
55
+ const sq = modelElement.getAttribute('start');
56
+ if (sq != null) attrs['start'] = String(sq);
57
+
58
+ return writer.createContainerElement('div', attrs)
59
+ }
60
+ });
61
+
62
+ editor.conversion.for('editingDowncast').elementToElement({
63
+ model: 'numList',
64
+ view: (modelElement, { writer }) => {
65
+ const attrs: Record<string, string> = { class: 'num-list' };
66
+
67
+ const ds = modelElement.getAttribute('data-style');
68
+ if (ds != null) attrs['data-style'] = String(ds);
69
+
70
+ const db = modelElement.getAttribute('data-block');
71
+ if (db != null) attrs['data-block'] = String(db);
72
+
73
+ const sq = modelElement.getAttribute('start');
74
+ if (sq != null) attrs['start'] = String(sq);
75
+
76
+ return writer.createContainerElement('div', attrs);
77
+ }
78
+ });
79
+
80
+ editor.conversion.for('dataDowncast').elementToElement({
81
+ model: 'numItem',
82
+ view: (modelElement, { writer }) =>
83
+ writer.createContainerElement('div', { class: 'num-li' })
84
+ });
85
+
86
+ editor.conversion.for('editingDowncast').elementToElement({
87
+ model: 'numItem',
88
+ view: (modelElement, { writer }) =>
89
+ writer.createContainerElement('div', { class: 'num-li' })
90
+ });
91
+
92
+ editor.conversion.for('upcast').elementToElement({
93
+ view: {
94
+ name: 'div',
95
+ classes: 'num-list',
96
+ },
97
+ model: (viewElement, { writer }) => {
98
+ const attrs: any = {};
99
+
100
+ const ds = viewElement.getAttribute('data-style');
101
+ if (ds != null) attrs.dataStyle = ds;
102
+
103
+ const db = viewElement.getAttribute('data-block');
104
+ if (db != null) attrs.dataBlock = db;
105
+
106
+ const sq = viewElement.getAttribute('start');
107
+ if (sq != null) attrs.start = sq;
108
+
109
+ return writer.createElement('numList', attrs);
110
+ }
111
+ });
112
+
113
+ editor.conversion.for('upcast').elementToElement({
114
+ view: {
115
+ name: 'div',
116
+ classes: 'num-li'
117
+ },
118
+ model: (viewElement, { writer }) => writer.createElement('numItem')
119
+ });
120
+
121
+ editor.conversion.for('upcast').elementToElement({
122
+ view: { name: 'ol' },
123
+ model: (viewElement, { writer }) => writer.createElement('numList')
124
+ });
125
+
126
+ editor.conversion.for('upcast').elementToElement({
127
+ view: { name: 'li' },
128
+ model: (viewElement, { writer }) => writer.createElement('numItem')
129
+ });
130
+
131
+ const viewDoc = editor.editing.view.document;
132
+
133
+ const selectionSpansBlocks = (model: any) => {
134
+ const sel = model.document.selection;
135
+ if (sel.isCollapsed) return false;
136
+ const blocks = Array.from(sel.getSelectedBlocks());
137
+ return blocks.length > 1;
138
+ };
139
+
140
+ const selectionTouchesElements = (model: any) => {
141
+ const sel = model.document.selection;
142
+ if (sel.isCollapsed) return false;
143
+ const range = sel.getFirstRange();
144
+ for (const item of range.getItems()) {
145
+ if (item.is?.('element')) return true;
146
+ }
147
+ return false;
148
+ };
149
+
150
+ function getNearestSealedListFromPos(pos: any): any | null {
151
+ // ancestors: [root, ..., parent]
152
+ const ancestors = pos.getAncestors({ includeSelf: false }); // CKEditor retorna de root -> parent
153
+ // percorre de baixo pra cima (mais próximo primeiro)
154
+ for (let i = ancestors.length - 1; i >= 0; i--) {
155
+ const node = ancestors[i];
156
+ if (node.is?.('element', 'numList') && !!node.getAttribute?.('dataBlock')) {
157
+ return node;
158
+ }
159
+ }
160
+ return null;
161
+ }
162
+
163
+ // Verifica se `node` está DENTRO do `list` (qualquer profundidade)
164
+ function isInside(node: any, ancestor: any): boolean {
165
+ let p = node;
166
+ while (p) {
167
+ if (p === ancestor) return true;
168
+ p = p.parent;
169
+ }
170
+ return false;
171
+ }
172
+
173
+ const firstItem = (list: any) => list.getChild(0);
174
+ const lastItem = (list: any) => list.getChild(list.childCount - 1);
175
+
176
+ function ensureTypablePosInItem(writer: any, item: any, atEnd = false) {
177
+ let p = null;
178
+ for (const c of item.getChildren()) {
179
+ if (c.is?.('element', 'paragraph')) { p = c; break; }
180
+ }
181
+ if (!p) {
182
+ p = writer.createElement('paragraph');
183
+ writer.insert(p, writer.createPositionAt(item, 0));
184
+ }
185
+ return atEnd
186
+ ? writer.createPositionAt(p, 'end')
187
+ : writer.createPositionAt(p, 0);
188
+ }
189
+
190
+ editor.plugins.get('ClipboardPipeline').on('inputTransformation', (evt: any, data: any) => {
191
+ const { model } = editor;
192
+ const sel = model.document.selection;
193
+ const pos = sel.getFirstPosition();
194
+ if (!pos) return;
195
+
196
+ const sealed = getNearestSealedListFromPos(pos);
197
+ if (!sealed) return;
198
+
199
+ const item = pos.findAncestor('numItem');
200
+ if (item && isInside(item, sealed)) return; // já dentro: ok
201
+
202
+ // redireciona para o primeiro item do sealed
203
+ evt.stop();
204
+ model.change((writer: any) => {
205
+ const target = firstItem(sealed) || writer.createElement('numItem');
206
+ if (!firstItem(sealed)) {
207
+ writer.insert(target, writer.createPositionAt(sealed, 0));
208
+ }
209
+ const at = ensureTypablePosInItem(writer, target, false);
210
+ writer.setSelection(at);
211
+ // reaplica como texto simples (ajuste conforme sua pipeline)
212
+ const text = data.dataTransfer.getData('text/plain');
213
+ if (text) editor.execute('input', { text });
214
+ });
215
+ });
216
+
217
+
218
+ // não permitir que saia da lista caso exista o atributo data-block
219
+ // viewDoc.on('keydown', (evt, data) => {
220
+ // if (data.keyCode !== keyCodes.enter || data.shiftKey) return;
221
+ // if (!editor.model.document.selection.isCollapsed) return;
222
+
223
+ // if(config?.disableEnter === true) {
224
+ // return;
225
+ // }
226
+
227
+ // const { model } = editor;
228
+ // const pos = model.document.selection.getFirstPosition();
229
+ // if (!pos) return;
230
+
231
+ // const sealed = getNearestSealedListFromPos(pos);
232
+ // if (!sealed) return; // só intercepta se estiver dentro de um sealed
233
+
234
+ // data.preventDefault(); evt.stop();
235
+
236
+ // model.change(writer => {
237
+ // // deixa o enter nativo dividir o bloco
238
+ // editor.execute('enter');
239
+
240
+ // const posAfter = model.document.selection.getFirstPosition();
241
+ // if (!posAfter) return;
242
+ // const newBlock = posAfter.parent as any;
243
+ // if (!newBlock) return;
244
+
245
+ // // queremos transformar o novo bloco em um novo numItem,
246
+ // // mantendo-o dentro de ALGUM numList que esteja dentro do `sealed` (o mais próximo)
247
+ // // pega o numItem atual (mais próximo)
248
+ // let currentItem = posAfter.findAncestor('numItem');
249
+
250
+ // // se o bloco recém criado ficou fora de um numItem,
251
+ // // cria um numItem irmão do atual (se existir) dentro do mesmo numList
252
+ // if (!currentItem) {
253
+ // const listForNew = getNearestSealedListFromPos(posAfter) || sealed;
254
+ // const newItem = writer.createElement('numItem');
255
+ // writer.insert(newItem, writer.createPositionAt(listForNew, 'end'));
256
+ // writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
257
+ // writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
258
+ // return;
259
+ // }
260
+
261
+ // // garantir que este numItem pertence a um numList que está dentro do sealed mais próximo
262
+ // let itsList = currentItem.parent; // deve ser um numList
263
+ // if (!isInside(itsList, sealed)) {
264
+ // // se por alguma razão o split empurrou pra fora, anexa de volta ao sealed
265
+ // const fallback = firstItem(sealed) || null;
266
+ // if (fallback) {
267
+ // const newItem = writer.createElement('numItem');
268
+ // writer.insert(newItem, writer.createPositionAfter(fallback));
269
+ // writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
270
+ // writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
271
+ // } else {
272
+ // // sealed vazio (raro): crie o primeiro item
273
+ // const newItem = writer.createElement('numItem');
274
+ // writer.insert(newItem, writer.createPositionAt(sealed, 0));
275
+ // writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
276
+ // writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
277
+ // }
278
+ // return;
279
+ // }
280
+
281
+ // // caso normal: cria irmão dentro do mesmo numList
282
+ // const newItem = writer.createElement('numItem');
283
+ // writer.insert(newItem, writer.createPositionAfter(currentItem));
284
+ // writer.move(writer.createRangeOn(newBlock), writer.createPositionAt(newItem, 0));
285
+ // writer.setSelection(ensureTypablePosInItem(writer, newItem, false));
286
+ // });
287
+ // }, { priority: 'high' });
288
+
289
+ // não permitir que backspace/delete saia da lista caso exista o atributo data-block
290
+ viewDoc.on(
291
+ 'keydown',
292
+ (evt, data) => {
293
+ const isBackspace = data.keyCode === keyCodes.backspace;
294
+ const isDelete = data.keyCode === keyCodes.delete;
295
+ if (!isBackspace && !isDelete) return;
296
+ if (!editor.model.document.selection.isCollapsed) return;
297
+
298
+ const model = editor.model;
299
+ const sel = model.document.selection;
300
+
301
+ data.preventDefault();
302
+ evt.stop();
303
+
304
+ model.change( async (writer: any) => {
305
+ // @ts-ignore
306
+ const getPos = sel.getFirstPosition() as any;
307
+ const blocoPos = getPos?.findAncestor('paragraph') ?? getPos?.findAncestor('numItem') ?? getPos?.findAncestor('numList') ?? null;
308
+ const itemPos = blocoPos.findAncestor('numItem');
309
+ const listPos = itemPos.findAncestor('numList');
310
+
311
+ // @ts-ignore
312
+ editor.execute(isBackspace ? 'delete' : 'deleteForward', { unit: 'character', direction: isBackspace ? 'backward' : 'forward' });
313
+
314
+ if(blocoPos.is('element', 'paragraph') && !blocoPos?.getChild(0)) {
315
+ writer.remove(blocoPos);
316
+ }
317
+ if(itemPos && itemPos.childCount === 0) {
318
+ writer.remove(itemPos);
319
+ }
320
+ if(listPos && listPos.childCount === 0) {
321
+ writer.remove(listPos);
322
+ }
323
+
324
+
325
+ });
326
+
327
+ if(isBackspace) {
328
+ model.change( async (writer: any) => {
329
+ // coloca o cursor no final do bloco atual
330
+ const posAfter = model.document.selection.getFirstPosition() as any;
331
+ writer.setSelection(writer.createPositionAt(posAfter.parent, 'end'));
332
+ });
333
+ }
334
+
335
+ return;
336
+
337
+ },
338
+ { priority: 'high' }
339
+ );
340
+
341
+ /* Ação ao pressionar a tecla TAB */
342
+ viewDoc.on(
343
+ 'keydown',
344
+ (evt, data) => {
345
+ if (!(data.keyCode == keyCodes.tab && !data.shiftKey)) return;
346
+ if (!editor.model.document.selection.isCollapsed) return;
347
+
348
+ const model = editor.model;
349
+
350
+ const posBefore = model.document.selection.getFirstPosition();
351
+ const inItem = posBefore?.findAncestor('numItem');
352
+ const inList = posBefore?.findAncestor('numList');
353
+ if (!inItem || !inList) return; // fora da nossa lista -> deixa o Tab padrão agir
354
+
355
+ data.preventDefault();
356
+ evt.stop();
357
+
358
+ editor.execute('toggleNumberedDivList');
359
+ },
360
+ { priority: 'high' }
361
+ );
362
+
363
+ /* Ação ao pressionar as teclas Shift + Tab */
364
+ viewDoc.on(
365
+ 'keydown',
366
+ (evt, data) => {
367
+ if (data.keyCode !== keyCodes.tab || !data.shiftKey) return;
368
+ if (!editor.model.document.selection.isCollapsed) return;
369
+
370
+ const posBefore = editor.model.document.selection.getFirstPosition();
371
+ const inItem = posBefore?.findAncestor('numItem');
372
+ const inList = posBefore?.findAncestor('numList');
373
+ if (!inItem || !inList) return;
374
+
375
+ data.preventDefault();
376
+ evt.stop();
377
+
378
+ shiftTab(editor);
379
+
380
+ },
381
+ { priority: 'high' }
382
+ );
383
+
384
+
385
+ editor.model.document.on('change:data', () => {
386
+ executeForceList(editor, config);
387
+ });
388
+
389
+
390
+ editor.model.schema.extend('$text', {
391
+ allowAttributes: ['dataBlock', 'start']
392
+ });
393
+
394
+ // quando o editor for inicializado, dispara o change:data uma vez
395
+ editor.on('ready', () => {
396
+ executeForceList(editor, config);
397
+ });
398
+
399
+
400
+ // @ts-ignore
401
+ editor.commands.add('toggleNumberedDivList', new ToggleNumberedDivListCommand(editor));
402
+ }
403
+ }
404
+
405
+ class ToggleNumberedDivListCommand {
406
+ declare editor: any;
407
+ declare value: boolean;
408
+ declare isEnabled: boolean;
409
+
410
+ constructor(editor: any) {
411
+ this.editor = editor;
412
+ this.value = false;
413
+ this.isEnabled = true;
414
+ }
415
+
416
+ refresh() {
417
+ this.isEnabled = true;
418
+ }
419
+
420
+ execute(options?: { value?: string; start?: number } ): void {
421
+ const { value, start } = options || {};
422
+ const editor = this?.editor || null;
423
+ const model = editor.model;
424
+
425
+ const selection = model.document.selection;
426
+ const firstPos = selection.getFirstPosition('paragraph') ?? selection.getFirstPosition('numItem');
427
+ const existingItem = firstPos?.findAncestor('numItem');
428
+ const paragraphAbove = firstPos?.findAncestor('paragraph');
429
+ const firstParagraphItem = existingItem ? existingItem?.getChild(0) : null;
430
+ let existingList = firstPos?.findAncestor('numList');
431
+ let firstItemInList = existingList ? existingList.getChild(0) : null;
432
+ const selectedItemIndex = existingList ? existingList.getChildIndex(existingItem) : null;
433
+
434
+ if(value === 'recuar') {
435
+ if (!existingItem || !existingList) return;
436
+ if(value === 'recuar') return shiftTab(editor);
437
+ }
438
+
439
+ if(value !== undefined) {
440
+ const block = firstPos.parent as any;
441
+
442
+ if(existingItem && firstItemInList === existingItem) {
443
+ if(value === 'decimal') {
444
+ return;
445
+ }
446
+ }
447
+
448
+ if(existingItem?.getChild(0) === block) {
449
+ return editor.execute('toggleNumberedDivList', { value: undefined, start: start });
450
+ }
451
+ }
452
+
453
+ model.change((writer: any) => {
454
+ // se não for o primeiro do item
455
+ if ((value == undefined || value == 'decimal') && paragraphAbove === firstParagraphItem && selectedItemIndex >= 0) {
456
+
457
+ if(!selectedItemIndex) return;
458
+
459
+ // procura o numItem acima da seleção
460
+ let prevItem = existingList.getChild(selectedItemIndex - 1);
461
+ if (!prevItem) return;
462
+
463
+ if(prevItem.is('element', 'numItem')) {
464
+ // criar uma nova lista abaixo da prevItem
465
+ const newBlock = writer.createElement('numList', {
466
+ ...(value && value !== 'decimal' ? { 'data-style': value } : {})
467
+ });
468
+ const insertPos = writer.createPositionAfter(prevItem);
469
+ writer.insert(newBlock, insertPos);
470
+
471
+ writer.move(writer.createRangeOn(existingItem), writer.createPositionAt(newBlock, 0));
472
+ } else {
473
+ // move para o final do prevItem
474
+ writer.move(writer.createRangeOn(existingItem), writer.createPositionAt(prevItem, 'end'));
475
+ }
476
+
477
+ return;
478
+ }
479
+
480
+ const blocks = Array.from(selection.getSelectedBlocks()) as any[];
481
+
482
+ if (!blocks.length) {
483
+ const insertRange = findOptimalInsertionRange(selection, model);
484
+ const numList = writer.createElement('numList');
485
+ const numItem = writer.createElement('numItem');
486
+ writer.insert(numList, insertRange.start);
487
+ writer.insert(numItem, writer.createPositionAt(numList, 0));
488
+ const paragraph = writer.createElement('paragraph');
489
+ writer.insert(paragraph, writer.createPositionAt(numItem, 0));
490
+ writer.setSelection(paragraph, 'in');
491
+ return;
492
+ }
493
+
494
+ if (existingList && value === undefined) {
495
+ // verifica se está dentro de um numItem
496
+ const isInsideNumItem = blocks[0].findAncestor('numItem') === existingItem;
497
+ let insertPos = writer.createPositionBefore(paragraphAbove);
498
+ let numItem;
499
+
500
+ if (isInsideNumItem && existingItem) {
501
+ // cria um num item abaixo do existente
502
+ numItem = writer.createElement('numItem');
503
+ insertPos = writer.createPositionAfter(existingItem);
504
+ writer.insert(numItem, insertPos);
505
+ } else {
506
+ numItem = writer.createElement('numItem');
507
+ }
508
+
509
+ for (const block of blocks) {
510
+ writer.insert(numItem, insertPos);
511
+ writer.move(writer.createRangeOn(block), writer.createPositionAt(numItem, 0));
512
+ }
513
+ return;
514
+ }
515
+
516
+ // verifica se existe algum numList com data-first dentro de todo o conteúdo selecionado
517
+ let first = true;
518
+ for (const block of blocks) {
519
+ // @ts-ignore
520
+ const foundList = block.findAncestor('numList');
521
+ if (foundList) {
522
+ first = false;
523
+ break;
524
+ }
525
+ }
526
+
527
+ const firstBlock = blocks[0];
528
+ const listPos = writer.createPositionBefore(firstBlock);
529
+ const numList = writer.createElement('numList', {
530
+ ...(first && !value ? { 'data-block': 'true' } : {}),
531
+ ...(first && !start ? { 'start': start } : {}),
532
+ ...((value && value !== 'recuar' && value !== 'decimal') ? { 'data-style': value } : {})
533
+ });
534
+
535
+ writer.insert(numList, listPos);
536
+
537
+ for (const block of blocks) {
538
+ const numItem = writer.createElement('numItem');
539
+ writer.insert(numItem, writer.createPositionAt(numList, 'end'));
540
+ writer.move(writer.createRangeOn(block), writer.createPositionAt(numItem, 0));
541
+ }
542
+ });
543
+ }
544
+ }
545
+
546
+ function shiftTab(editor: any) {
547
+ const model = editor.model;
548
+
549
+ const selection = model.document.selection;
550
+ const firstBlock = selection.getFirstPosition('paragraph') ?? selection.getFirstPosition('numItem');
551
+ const firstPos = firstBlock.is('element', 'numItem') ? firstBlock : firstBlock.findAncestor('numItem');
552
+ const existingList = firstPos?.findAncestor('numList');
553
+ const selectedItemIndex = existingList ? existingList.getChildIndex(firstPos) : null;
554
+ const numListPai = existingList ? existingList?.findAncestor('numList') : null;
555
+ const selectedListPaiIndex = numListPai ? numListPai.getChildIndex(existingList) : null;
556
+
557
+ model.change((writer: any) => {
558
+ // se for o primeiro item da lista
559
+ if(numListPai && selectedListPaiIndex >= 0 && selectedItemIndex === 0) {
560
+ writer.move(writer.createRangeOn(firstPos), writer.createPositionAt(numListPai, selectedListPaiIndex));
561
+ return;
562
+ }
563
+ // se for o último item da lista
564
+ if(numListPai && selectedListPaiIndex >= 0 && selectedItemIndex === existingList.childCount -1) {
565
+ writer.move(writer.createRangeOn(firstPos), writer.createPositionAt(numListPai, selectedListPaiIndex + 1));
566
+ return;
567
+ }
568
+
569
+ // criar novas regras de acordo com a necessidade
570
+
571
+ });
572
+ }
573
+
574
+ function executeForceList(editor: any, config: any) {
575
+ const model = editor.model;
576
+ const firstElement = editor.model.document.getRoot().getChild(0) as any;
577
+ if(config?.forceList && firstElement && firstElement.name !== 'numList') {
578
+ const numList = model.change( (writer: any) => {
579
+ const insertPos = writer.createPositionAt(model.document.getRoot(), 0);
580
+ const numList = writer.createElement('numList', {
581
+ 'data-block': 'true',
582
+ 'start': config?.forceList ? config.forceList + 1 : null
583
+ });
584
+ writer.insert(numList, insertPos);
585
+ return numList;
586
+ });
587
+
588
+ model.change( (writer: any) => {
589
+ const root = model.document.getRoot();
590
+ const itemsToMove = [];
591
+ for ( const child of root.getChildren() ) {
592
+ if ( child !== numList ) {
593
+ itemsToMove.push(child);
594
+ }
595
+ }
596
+ for ( const item of itemsToMove ) {
597
+ const numItem = writer.createElement('numList');
598
+ writer.insert( numItem, writer.createPositionAt( numList, 'end' ) );
599
+ writer.move( writer.createRangeOn( item ), writer.createPositionAt( numItem, 0 ) );
600
+ }
601
+ });
602
+ }
603
+ }