UNPKG

4.28 kBJavaScriptView Raw
1/**
2 * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5import { iterateSiblingListBlocks } from './listwalker';
6import { getListItemBlocks, isListItemBlock, ListItemUid } from './model';
7/**
8 * Based on the provided positions looks for the list head and stores it in the provided map.
9 *
10 * @internal
11 * @param position The search starting position.
12 * @param itemToListHead The map from list item element to the list head element.
13 */
14export function findAndAddListHeadToMap(position, itemToListHead) {
15 const previousNode = position.nodeBefore;
16 if (!isListItemBlock(previousNode)) {
17 const item = position.nodeAfter;
18 if (isListItemBlock(item)) {
19 itemToListHead.set(item, item);
20 }
21 }
22 else {
23 let listHead = previousNode;
24 // Previously, the loop below was defined like this:
25 //
26 // for ( { node: listHead } of iterateSiblingListBlocks( listHead, 'backward' ) )
27 //
28 // Unfortunately, such a destructuring is incorrectly transpiled by Babel and the loop never ends.
29 // See: https://github.com/ckeditor/ckeditor5-react/issues/345.
30 for (const { node } of iterateSiblingListBlocks(listHead, 'backward')) {
31 listHead = node;
32 if (itemToListHead.has(listHead)) {
33 return;
34 }
35 }
36 itemToListHead.set(previousNode, listHead);
37 }
38}
39/**
40 * Scans the list starting from the given list head element and fixes items' indentation.
41 *
42 * @internal
43 * @param listNodes The iterable of list nodes.
44 * @param writer The model writer.
45 * @returns Whether the model was modified.
46 */
47export function fixListIndents(listNodes, writer) {
48 let maxIndent = 0; // Guards local sublist max indents that need fixing.
49 let prevIndent = -1; // Previous item indent.
50 let fixBy = null;
51 let applied = false;
52 for (const { node } of listNodes) {
53 const itemIndent = node.getAttribute('listIndent');
54 if (itemIndent > maxIndent) {
55 let newIndent;
56 if (fixBy === null) {
57 fixBy = itemIndent - maxIndent;
58 newIndent = maxIndent;
59 }
60 else {
61 if (fixBy > itemIndent) {
62 fixBy = itemIndent;
63 }
64 newIndent = itemIndent - fixBy;
65 }
66 if (newIndent > prevIndent + 1) {
67 newIndent = prevIndent + 1;
68 }
69 writer.setAttribute('listIndent', newIndent, node);
70 applied = true;
71 prevIndent = newIndent;
72 }
73 else {
74 fixBy = null;
75 maxIndent = itemIndent + 1;
76 prevIndent = itemIndent;
77 }
78 }
79 return applied;
80}
81/**
82 * Scans the list starting from the given list head element and fixes items' types.
83 *
84 * @internal
85 * @param listNodes The iterable of list nodes.
86 * @param seenIds The set of already known IDs.
87 * @param writer The model writer.
88 * @returns Whether the model was modified.
89 */
90export function fixListItemIds(listNodes, seenIds, writer) {
91 const visited = new Set();
92 let applied = false;
93 for (const { node } of listNodes) {
94 if (visited.has(node)) {
95 continue;
96 }
97 let listType = node.getAttribute('listType');
98 let listItemId = node.getAttribute('listItemId');
99 // Use a new ID if this one was spot earlier (even in other list).
100 if (seenIds.has(listItemId)) {
101 listItemId = ListItemUid.next();
102 }
103 seenIds.add(listItemId);
104 for (const block of getListItemBlocks(node, { direction: 'forward' })) {
105 visited.add(block);
106 // Use a new ID if a block of a bigger list item has different type.
107 if (block.getAttribute('listType') != listType) {
108 listItemId = ListItemUid.next();
109 listType = block.getAttribute('listType');
110 }
111 if (block.getAttribute('listItemId') != listItemId) {
112 writer.setAttribute('listItemId', listItemId, block);
113 applied = true;
114 }
115 }
116 }
117 return applied;
118}