u2ui 1.5.2 → 1.5.4
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.
- package/el/ico/ico.js +5 -5
- package/el/rte/README.md +53 -13
- package/el/rte/manifest.json +19 -4
- package/el/rte/rte.css +31 -6
- package/el/rte/rte.js +174 -44
- package/el/rte0/README.md +37 -0
- package/el/rte0/manifest.json +33 -0
- package/el/rte0/rte0.css +9 -0
- package/el/rte0/rte0.js +56 -0
- package/el/system/styler.js +2 -0
- package/el/tooltip/tooltip.css +2 -2
- package/js/Placer/Placer.js +2 -1
- package/js/rte/AGENTS.md +142 -0
- package/js/rte/PLAN.md +488 -0
- package/js/rte/README.md +319 -25
- package/js/rte/blocks.js +7 -0
- package/js/rte/breaks.js +6 -0
- package/js/rte/classes.js +7 -0
- package/js/rte/docs/usage.md +845 -0
- package/js/rte/images.js +7 -0
- package/js/rte/link.js +8 -0
- package/js/rte/playground/README.md +95 -0
- package/js/rte/playground/hosts.html +518 -0
- package/js/rte/playground/index.html +824 -0
- package/js/rte/rte.js +56 -5
- package/js/rte/source.js +12 -0
- package/js/rte/src/browser/README.md +40 -0
- package/js/rte/src/browser/range-rect.js +82 -0
- package/js/rte/src/client/README.md +378 -0
- package/js/rte/src/client/blocks.js +38 -0
- package/js/rte/src/client/breaks.js +212 -0
- package/js/rte/src/client/classes.js +48 -0
- package/js/rte/src/client/editor.js +461 -0
- package/js/rte/src/client/external-input.js +47 -0
- package/js/rte/src/client/history.js +13 -0
- package/js/rte/src/client/images.js +275 -0
- package/js/rte/src/client/link.js +572 -0
- package/js/rte/src/client/marks.js +25 -0
- package/js/rte/src/client/source.js +182 -0
- package/js/rte/src/client/structure.js +27 -0
- package/js/rte/src/client/tables.js +162 -0
- package/js/rte/src/client/unstyle.js +32 -0
- package/js/rte/src/command/README.md +517 -0
- package/js/rte/src/command/block-boundary.js +43 -0
- package/js/rte/src/command/block-style.js +120 -0
- package/js/rte/src/command/commands.js +158 -0
- package/js/rte/src/command/delete.js +112 -0
- package/js/rte/src/command/edit.js +72 -0
- package/js/rte/src/command/element.js +45 -0
- package/js/rte/src/command/enter.js +175 -0
- package/js/rte/src/command/fragment.js +76 -0
- package/js/rte/src/command/insert.js +52 -0
- package/js/rte/src/command/list.js +284 -0
- package/js/rte/src/command/mark.js +583 -0
- package/js/rte/src/command/pending-marks.js +216 -0
- package/js/rte/src/command/table.js +207 -0
- package/js/rte/src/command/unstyle.js +248 -0
- package/js/rte/src/config/README.md +96 -0
- package/js/rte/src/config/config.js +127 -0
- package/js/rte/src/core/README.md +41 -0
- package/js/rte/src/core/core.js +156 -0
- package/js/rte/src/history/README.md +83 -0
- package/js/rte/src/history/history.js +283 -0
- package/js/rte/src/input/README.md +155 -0
- package/js/rte/src/input/external-input.js +130 -0
- package/js/rte/src/input/input-pipeline.js +414 -0
- package/js/rte/src/mark/README.md +219 -0
- package/js/rte/src/mark/dom-adapter.js +100 -0
- package/js/rte/src/mark/mark.js +133 -0
- package/js/rte/src/mark/standard.js +76 -0
- package/js/rte/src/model/README.md +71 -0
- package/js/rte/src/model/content-model.js +213 -0
- package/js/rte/src/model/html/README.md +51 -0
- package/js/rte/src/model/html/html-model.js +90 -0
- package/js/rte/src/normalize/execute/README.md +33 -0
- package/js/rte/src/normalize/execute/repair-executor.js +129 -0
- package/js/rte/src/normalize/normalizer/README.md +48 -0
- package/js/rte/src/normalize/normalizer/normalizer.js +141 -0
- package/js/rte/src/normalize/repair/README.md +70 -0
- package/js/rte/src/normalize/repair/repair-planner.js +208 -0
- package/js/rte/src/sanitize/README.md +105 -0
- package/js/rte/src/sanitize/native.js +43 -0
- package/js/rte/src/sanitize/policy.js +202 -0
- package/js/rte/src/selection/README.md +39 -0
- package/js/rte/src/selection/map/README.md +53 -0
- package/js/rte/src/selection/map/point-map.js +307 -0
- package/js/rte/src/selection/ownership/README.md +32 -0
- package/js/rte/src/selection/ownership/ownership.js +42 -0
- package/js/rte/src/selection/point/README.md +34 -0
- package/js/rte/src/selection/point/point.js +83 -0
- package/js/rte/src/selection/range/README.md +38 -0
- package/js/rte/src/selection/range/edit-range.js +223 -0
- package/js/rte/src/selection/snapshot.js +64 -0
- package/js/rte/src/source/README.md +64 -0
- package/js/rte/src/source/source.js +193 -0
- package/js/rte/src/surface/README.md +36 -0
- package/js/rte/src/surface/content.js +16 -0
- package/js/rte/src/surface/surface.js +87 -0
- package/js/rte/src/transaction/README.md +35 -0
- package/js/rte/src/transaction/transaction.js +56 -0
- package/js/rte/src/ui/README.md +255 -0
- package/js/rte/src/ui/chrome.js +214 -0
- package/js/rte/src/ui/handles.js +179 -0
- package/js/rte/src/ui/place.js +25 -0
- package/js/rte/src/ui/toolbar.js +283 -0
- package/js/rte/src/unstyle/README.md +103 -0
- package/js/rte/src/unstyle/unstyle.js +140 -0
- package/js/rte/tables.js +7 -0
- package/js/rte/unstyle.js +8 -0
- package/js/rte0/README.md +37 -0
- package/js/rte0/rte.js +7 -0
- package/js/{rte → rte0}/src/behavior.js +0 -1
- package/js/{rte → rte0}/src/events.js +0 -1
- package/js/{rte → rte0}/src/range.js +0 -2
- package/js/{rte → rte0}/src/ui.items.js +0 -2
- package/js/{rte → rte0}/src/ui.js +0 -1
- package/package.json +1 -1
- package/u2/Element.js +16 -4
- package/u2/enhance.js +2 -1
- package/u2/plan-scoped-refactoring.md +133 -304
- package/u2/projects.json +18 -2
- package/u2/tools/update.repos.json.js +1 -1
- package/u2/utils.js +0 -16
- package/el/qrcode/package.json +0 -22
- /package/js/{rte → rte0}/old/NodeCleaner.js +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.items.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/Rte.ui.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/crossbrowser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.svg +0 -0
- /package/js/{rte → rte0}/old/Rte/font/tinymce.woff +0 -0
- /package/js/{rte → rte0}/old/Rte/helpers.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/htmlparser.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/index.mjs +0 -0
- /package/js/{rte → rte0}/old/Rte/main.css +0 -0
- /package/js/{rte → rte0}/old/old.rte.js +0 -0
- /package/js/{rte → rte0}/old/range-selection.old.js +0 -0
- /package/js/{rte → rte0}/src/fixes.js +0 -0
- /package/js/{rte → rte0}/src/rte.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/ai.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/code.js +0 -0
- /package/js/{rte → rte0}/src/ui/items/undo.js +0 -0
- /package/js/{rte → rte0}/src/util/htmlparser.js +0 -0
- /package/js/{rte → rte0}/src/util/indentHtml.js +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {Point} from '../selection/point/point.js';
|
|
2
|
+
import {fill} from './block-boundary.js';
|
|
3
|
+
|
|
4
|
+
// Inserts one prepared element at the selection. The content model decides
|
|
5
|
+
// where it belongs: the block is split only as far as the nearest container
|
|
6
|
+
// that accepts the element, so a block-level rule separates paragraphs while an
|
|
7
|
+
// inline element stays inside its text.
|
|
8
|
+
//
|
|
9
|
+
// A selection is not deleted first: the element lands where the selection
|
|
10
|
+
// starts and the selected content stays after it. Insertion may not depend on a
|
|
11
|
+
// caret — a toolbar shown only for a selection would never offer it.
|
|
12
|
+
export function insertNode(create, inputTypes = []) {
|
|
13
|
+
if (typeof create !== 'function') throw new TypeError('Node insertion requires a factory function');
|
|
14
|
+
if (!Array.isArray(inputTypes)) throw new TypeError('Input types must be an array');
|
|
15
|
+
return {
|
|
16
|
+
inputTypes: [...inputTypes],
|
|
17
|
+
enabled: edit => !!container(edit, create(edit.document)),
|
|
18
|
+
run: edit => insert(edit, create(edit.document)),
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function insert(edit, node) {
|
|
23
|
+
const parent = container(edit, node);
|
|
24
|
+
if (!parent) return null;
|
|
25
|
+
const start = edit.range.start;
|
|
26
|
+
const caret = new Point(start.node, start.offset, 'forward');
|
|
27
|
+
edit.map.add(caret);
|
|
28
|
+
const at = edit.map.split(parent, start.node, start.offset);
|
|
29
|
+
edit.map.insert(parent, at, node);
|
|
30
|
+
const position = edit.map.get(caret);
|
|
31
|
+
// Splitting at a block edge leaves an empty half that needs a caret.
|
|
32
|
+
for (const sibling of [parent.childNodes[at - 1], parent.childNodes[at + 1]]) {
|
|
33
|
+
if (sibling?.nodeType === Node.ELEMENT_NODE && edit.model.block(sibling)) fill(edit, sibling);
|
|
34
|
+
}
|
|
35
|
+
edit.transaction.touch(parent);
|
|
36
|
+
edit.select(position);
|
|
37
|
+
return node;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function container(edit, node) {
|
|
41
|
+
const start = edit.range?.start;
|
|
42
|
+
if (!start) return null;
|
|
43
|
+
const from = start.node.nodeType === Node.ELEMENT_NODE ? start.node : start.node.parentElement;
|
|
44
|
+
for (let element = from; element && element !== edit.element; element = element.parentElement) {
|
|
45
|
+
if (edit.model.atomic(element)) return null;
|
|
46
|
+
}
|
|
47
|
+
for (let element = from; element; element = element.parentElement) {
|
|
48
|
+
if (edit.model.allows(element, node)) return element;
|
|
49
|
+
if (element === edit.element) return null;
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import {Point, indexOf} from '../selection/point/point.js';
|
|
2
|
+
import {emptyBlock} from './block-boundary.js';
|
|
3
|
+
|
|
4
|
+
const TAG = /^[a-z][a-z\d-]*$/;
|
|
5
|
+
|
|
6
|
+
// A closed group of list container elements. The group decides what counts as
|
|
7
|
+
// an existing list, so a layout element the content model also calls a block is
|
|
8
|
+
// never mistaken for one. Item elements come from the model's default child, so
|
|
9
|
+
// the same commands serve any configured list-like structure.
|
|
10
|
+
export class Lists {
|
|
11
|
+
#tags;
|
|
12
|
+
#indent;
|
|
13
|
+
#outdent;
|
|
14
|
+
|
|
15
|
+
constructor(tags = ['ul', 'ol']) {
|
|
16
|
+
if (!Array.isArray(tags) || !tags.length) throw new TypeError('Lists require a non-empty array of tag names');
|
|
17
|
+
if (tags.some(tag => typeof tag !== 'string' || !TAG.test(tag.trim().toLowerCase()))) {
|
|
18
|
+
throw new TypeError('A list tag must be a usable tag name');
|
|
19
|
+
}
|
|
20
|
+
this.#tags = Object.freeze([...new Set(tags.map(tag => tag.trim().toLowerCase()))]);
|
|
21
|
+
this.#indent = {
|
|
22
|
+
inputTypes: ['formatIndent'],
|
|
23
|
+
enabled: edit => runs(this.#units(edit)).some(run => !!this.#previous(edit, run[0])),
|
|
24
|
+
run: edit => this.#change(edit, runs(this.#units(edit)), run => this.#nest(edit, run)),
|
|
25
|
+
};
|
|
26
|
+
this.#outdent = {
|
|
27
|
+
inputTypes: ['formatOutdent'],
|
|
28
|
+
enabled: edit => runs(this.#units(edit)).some(run => this.#raisable(edit, run)),
|
|
29
|
+
run: edit => this.#change(edit, runs(this.#units(edit)), run => this.#raise(edit, run)),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get tags() { return [...this.#tags]; }
|
|
34
|
+
get indent() { return this.#indent; }
|
|
35
|
+
get outdent() { return this.#outdent; }
|
|
36
|
+
|
|
37
|
+
// Turns the selected blocks into a list of `tag`, converts an existing list
|
|
38
|
+
// of another kind, and lifts its items back out when it is already active.
|
|
39
|
+
toggle(tag) {
|
|
40
|
+
const container = String(tag).trim().toLowerCase();
|
|
41
|
+
if (!this.#tags.includes(container)) throw new RangeError(`Unknown list tag: ${container}`);
|
|
42
|
+
return {
|
|
43
|
+
enabled: edit => this.#enabled(edit, container),
|
|
44
|
+
state: edit => this.#state(edit, container),
|
|
45
|
+
run: edit => {
|
|
46
|
+
const units = this.#units(edit);
|
|
47
|
+
// The branch is decided once: changing the first run would
|
|
48
|
+
// otherwise report a different state to the second.
|
|
49
|
+
const lift = this.#state(edit, container, units) === true;
|
|
50
|
+
return this.#change(edit, runs(units), run => lift
|
|
51
|
+
? this.#lift(edit, run)
|
|
52
|
+
: this.#apply(edit, run, container));
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// The block scan is the expensive part of every query, so it is done once
|
|
58
|
+
// and handed on rather than repeated by each step.
|
|
59
|
+
#enabled(edit, container) {
|
|
60
|
+
const units = this.#units(edit);
|
|
61
|
+
const groups = runs(units);
|
|
62
|
+
if (!groups.length) return false;
|
|
63
|
+
return this.#state(edit, container, units) === true
|
|
64
|
+
? groups.every(run => this.#liftable(edit, run))
|
|
65
|
+
: groups.some(run => this.#appliable(edit, run, container));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#state(edit, container, units = this.#units(edit)) {
|
|
69
|
+
if (!units.length) return null;
|
|
70
|
+
let value = null;
|
|
71
|
+
for (const unit of units) {
|
|
72
|
+
const active = this.#list(edit, unit)?.localName === container;
|
|
73
|
+
if (value !== null && value !== active) return 'mixed';
|
|
74
|
+
value = active;
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// One unit per selected block: the list item that owns it, or the block.
|
|
80
|
+
#units(edit) {
|
|
81
|
+
if (!edit?.range) return [];
|
|
82
|
+
const blocks = edit.range.blocks(element => element !== edit.element && edit.model.block(element));
|
|
83
|
+
const units = new Set();
|
|
84
|
+
for (const block of blocks) units.add(this.#unit(edit, block));
|
|
85
|
+
return [...units];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#unit(edit, block) {
|
|
89
|
+
for (let element = block; element && element !== edit.element; element = element.parentElement) {
|
|
90
|
+
if (this.#list(edit, element)) return element;
|
|
91
|
+
}
|
|
92
|
+
return block;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// The list an element is an item of, or null.
|
|
96
|
+
#list(edit, element) {
|
|
97
|
+
const list = element?.parentElement;
|
|
98
|
+
if (!list || !this.#tags.includes(list.localName)) return null;
|
|
99
|
+
return edit.model.defaultChild(list) === element.localName ? list : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#previous(edit, item) {
|
|
103
|
+
const list = this.#list(edit, item);
|
|
104
|
+
const previous = item.previousElementSibling;
|
|
105
|
+
return list && previous && this.#list(edit, previous) === list ? previous : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#raisable(edit, run) {
|
|
109
|
+
const list = this.#list(edit, run[0]);
|
|
110
|
+
if (!list) return false;
|
|
111
|
+
return !!this.#list(edit, list.parentElement) || this.#liftable(edit, run);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#liftable(edit, run) {
|
|
115
|
+
const list = this.#list(edit, run[0]);
|
|
116
|
+
if (!list) return false;
|
|
117
|
+
const parent = list.parentElement;
|
|
118
|
+
return run.every(item => !!ownBlocks(edit, item, parent) || this.#rewritable(edit, parent));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// An item with nothing that can stand on its own becomes the host's
|
|
122
|
+
// configured text block instead.
|
|
123
|
+
#rewritable(edit, parent) {
|
|
124
|
+
const tag = edit.config.block;
|
|
125
|
+
return !!tag && edit.model.allows(parent, edit.document.createElement(tag));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#appliable(edit, run, container) {
|
|
129
|
+
const replacement = edit.document.createElement(container);
|
|
130
|
+
const list = this.#list(edit, run[0]);
|
|
131
|
+
if (list) return list.localName !== container && edit.model.allows(list.parentElement, replacement);
|
|
132
|
+
return !!edit.model.defaultChild(container) && edit.model.allows(run[0].parentNode, replacement);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#change(edit, groups, change) {
|
|
136
|
+
if (!groups.length) return [];
|
|
137
|
+
const start = new Point(edit.range.start.node, edit.range.start.offset, 'forward');
|
|
138
|
+
const end = new Point(edit.range.end.node, edit.range.end.offset, 'backward');
|
|
139
|
+
const backward = !!edit.surface.selection?.backward;
|
|
140
|
+
edit.map.add(start).add(end);
|
|
141
|
+
const changed = [];
|
|
142
|
+
for (const run of groups) changed.push(...change(run));
|
|
143
|
+
edit.select(edit.map.get(start), edit.map.get(end), backward);
|
|
144
|
+
return changed;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
#apply(edit, run, container) {
|
|
148
|
+
if (!this.#appliable(edit, run, container)) return [];
|
|
149
|
+
const replacement = edit.document.createElement(container);
|
|
150
|
+
const current = this.#list(edit, run[0]);
|
|
151
|
+
if (current) {
|
|
152
|
+
const isolated = this.#isolate(edit, run);
|
|
153
|
+
for (const attribute of isolated.attributes) replacement.setAttribute(attribute.name, attribute.value);
|
|
154
|
+
const parent = isolated.parentElement;
|
|
155
|
+
edit.map.replaceWrapper(isolated, replacement);
|
|
156
|
+
edit.transaction.touch(parent);
|
|
157
|
+
return [this.#join(edit, replacement)];
|
|
158
|
+
}
|
|
159
|
+
const item = edit.model.defaultChild(container);
|
|
160
|
+
const parent = run[0].parentNode;
|
|
161
|
+
edit.map.insert(parent, indexOf(run[0]), replacement);
|
|
162
|
+
for (const block of run) {
|
|
163
|
+
edit.map.move(this.#itemize(edit, block, item), replacement, replacement.childNodes.length);
|
|
164
|
+
}
|
|
165
|
+
edit.transaction.touch(parent);
|
|
166
|
+
return [this.#join(edit, replacement)];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// The host's neutral text block becomes the item itself; every other block
|
|
170
|
+
// keeps its own element and moves inside one.
|
|
171
|
+
#itemize(edit, block, tag) {
|
|
172
|
+
if (block.localName === tag) return block;
|
|
173
|
+
const item = edit.document.createElement(tag);
|
|
174
|
+
if (block.localName === edit.config.block
|
|
175
|
+
&& [...block.childNodes].every(child => edit.model.allows(item, child))) {
|
|
176
|
+
return edit.map.replaceWrapper(block, item);
|
|
177
|
+
}
|
|
178
|
+
edit.map.insert(block.parentNode, indexOf(block), item);
|
|
179
|
+
edit.map.move(block, item, 0);
|
|
180
|
+
return item;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#lift(edit, run) {
|
|
184
|
+
if (!this.#liftable(edit, run)) return [];
|
|
185
|
+
const isolated = this.#isolate(edit, run);
|
|
186
|
+
const parent = isolated.parentElement;
|
|
187
|
+
let at = indexOf(isolated);
|
|
188
|
+
const changed = [];
|
|
189
|
+
for (const item of [...isolated.children]) {
|
|
190
|
+
const nodes = ownBlocks(edit, item, parent)
|
|
191
|
+
|| (this.#rewritable(edit, parent)
|
|
192
|
+
? [edit.map.replaceWrapper(item, edit.document.createElement(edit.config.block))]
|
|
193
|
+
: []);
|
|
194
|
+
for (const node of nodes) {
|
|
195
|
+
edit.map.move(node, parent, at++);
|
|
196
|
+
changed.push(node);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
edit.map.remove(isolated);
|
|
200
|
+
edit.transaction.touch(parent);
|
|
201
|
+
return changed;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#nest(edit, run) {
|
|
205
|
+
const previous = this.#previous(edit, run[0]);
|
|
206
|
+
if (!previous) return [];
|
|
207
|
+
const tag = run[0].parentElement.localName;
|
|
208
|
+
let nested = previous.lastElementChild;
|
|
209
|
+
if (nested?.localName !== tag) {
|
|
210
|
+
nested = edit.document.createElement(tag);
|
|
211
|
+
if (!edit.model.allows(previous, nested)) return [];
|
|
212
|
+
edit.map.insert(previous, previous.childNodes.length, nested);
|
|
213
|
+
}
|
|
214
|
+
for (const item of run) edit.map.move(item, nested, nested.childNodes.length);
|
|
215
|
+
edit.transaction.touch(previous);
|
|
216
|
+
return [...run];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#raise(edit, run) {
|
|
220
|
+
const list = this.#list(edit, run[0]);
|
|
221
|
+
if (!list) return [];
|
|
222
|
+
const owner = list.parentElement;
|
|
223
|
+
if (!this.#list(edit, owner)) return this.#lift(edit, run);
|
|
224
|
+
const outer = owner.parentElement;
|
|
225
|
+
const isolated = this.#isolate(edit, run);
|
|
226
|
+
// Content after the nested list stays behind in a trailing item.
|
|
227
|
+
const at = edit.map.split(outer, owner, indexOf(isolated) + 1);
|
|
228
|
+
const trailing = outer.childNodes[at];
|
|
229
|
+
let target = at;
|
|
230
|
+
for (const item of [...isolated.children]) edit.map.move(item, outer, target++);
|
|
231
|
+
edit.map.remove(isolated);
|
|
232
|
+
for (const item of [owner, trailing]) {
|
|
233
|
+
if (item?.parentNode && emptyBlock(item, edit.model)) edit.map.remove(item);
|
|
234
|
+
}
|
|
235
|
+
edit.transaction.touch(outer);
|
|
236
|
+
return [...run];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Splits the surrounding list so the run becomes a list of its own.
|
|
240
|
+
#isolate(edit, run) {
|
|
241
|
+
const list = run[0].parentElement;
|
|
242
|
+
const parent = list.parentElement;
|
|
243
|
+
const end = indexOf(run.at(-1)) + 1;
|
|
244
|
+
if (end < list.childNodes.length) edit.map.split(parent, list, end);
|
|
245
|
+
const start = indexOf(run[0]);
|
|
246
|
+
return start > 0 ? parent.childNodes[edit.map.split(parent, list, start)] : list;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// A list that meets a list of its own kind is one list.
|
|
250
|
+
#join(edit, list) {
|
|
251
|
+
let result = list;
|
|
252
|
+
const previous = result.previousElementSibling;
|
|
253
|
+
if (previous?.localName === result.localName) {
|
|
254
|
+
while (result.firstChild) edit.map.move(result.firstChild, previous, previous.childNodes.length);
|
|
255
|
+
edit.map.remove(result);
|
|
256
|
+
result = previous;
|
|
257
|
+
}
|
|
258
|
+
const next = result.nextElementSibling;
|
|
259
|
+
if (next?.localName === result.localName) {
|
|
260
|
+
while (next.firstChild) edit.map.move(next.firstChild, result, result.childNodes.length);
|
|
261
|
+
edit.map.remove(next);
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Contiguous sibling units share one list, so they are changed together.
|
|
268
|
+
function runs(units) {
|
|
269
|
+
const groups = [];
|
|
270
|
+
for (const unit of units) {
|
|
271
|
+
const last = groups.at(-1);
|
|
272
|
+
if (last && last.at(-1).nextElementSibling === unit) last.push(unit);
|
|
273
|
+
else groups.push([unit]);
|
|
274
|
+
}
|
|
275
|
+
return groups;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// The item's own blocks, when every one of them may live in `parent` directly.
|
|
279
|
+
function ownBlocks(edit, item, parent) {
|
|
280
|
+
const children = [...item.childNodes].filter(node => !emptyBlock(node, edit.model));
|
|
281
|
+
const usable = children.length && children.every(node => node.nodeType === Node.ELEMENT_NODE
|
|
282
|
+
&& edit.model.block(node) && edit.model.allows(parent, node));
|
|
283
|
+
return usable ? children : null;
|
|
284
|
+
}
|