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 | */
|
5 | import { iterateSiblingListBlocks } from './listwalker';
|
6 | import { 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 | */
|
14 | export 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 | */
|
47 | export 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 | */
|
90 | export 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 | }
|