@thomasfosterau/effect-richtext 0.0.0 → 0.1.0
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/LICENSE +48 -0
- package/README.md +117 -1
- package/dist/_virtual/_rolldown/runtime.js +12 -0
- package/dist/commands/commands.d.ts +323 -0
- package/dist/commands/commands.js +722 -0
- package/dist/commands/internal.js +43 -0
- package/dist/commands/keymaps.d.ts +46 -0
- package/dist/commands/keymaps.js +64 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +3 -0
- package/dist/compat/commands.d.ts +250 -0
- package/dist/compat/commands.js +275 -0
- package/dist/compat/internal/core.d.ts +20 -0
- package/dist/compat/internal/core.js +148 -0
- package/dist/compat/internal/props.js +82 -0
- package/dist/compat/keymap.d.ts +21 -0
- package/dist/compat/keymap.js +38 -0
- package/dist/compat/model.d.ts +893 -0
- package/dist/compat/model.js +1212 -0
- package/dist/compat/orderedmap.d.ts +74 -0
- package/dist/compat/orderedmap.js +124 -0
- package/dist/compat/schema-basic.d.ts +38 -0
- package/dist/compat/schema-basic.js +238 -0
- package/dist/compat/schema-list.d.ts +81 -0
- package/dist/compat/schema-list.js +156 -0
- package/dist/compat/state.d.ts +415 -0
- package/dist/compat/state.js +689 -0
- package/dist/compat/test-builder.d.ts +98 -0
- package/dist/compat/test-builder.js +197 -0
- package/dist/compat/transform.d.ts +458 -0
- package/dist/compat/transform.js +803 -0
- package/dist/compat/view.d.ts +411 -0
- package/dist/compat/view.js +409 -0
- package/dist/dropcursor.d.ts +46 -0
- package/dist/dropcursor.js +176 -0
- package/dist/extensions/defaults.d.ts +716 -0
- package/dist/extensions/defaults.js +209 -0
- package/dist/extensions/editor.d.ts +246 -0
- package/dist/extensions/editor.js +271 -0
- package/dist/extensions/extension.d.ts +375 -0
- package/dist/extensions/extension.js +233 -0
- package/dist/extensions.d.ts +4 -0
- package/dist/extensions.js +4 -0
- package/dist/gapcursor.d.ts +66 -0
- package/dist/gapcursor.js +273 -0
- package/dist/history.d.ts +178 -0
- package/dist/history.js +412 -0
- package/dist/html.d.ts +158 -0
- package/dist/html.js +164 -0
- package/dist/inputrules.d.ts +271 -0
- package/dist/inputrules.js +321 -0
- package/dist/internal/attempt.js +15 -0
- package/dist/internal/json.js +29 -0
- package/dist/keymap/keymap.d.ts +130 -0
- package/dist/keymap/keymap.js +159 -0
- package/dist/keymap.d.ts +2 -0
- package/dist/keymap.js +1 -0
- package/dist/markdown/errors.d.ts +23 -0
- package/dist/markdown/errors.js +26 -0
- package/dist/markdown/from_markdown.d.ts +185 -0
- package/dist/markdown/from_markdown.js +266 -0
- package/dist/markdown/schema.d.ts +522 -0
- package/dist/markdown/schema.js +292 -0
- package/dist/markdown/to_markdown.d.ts +251 -0
- package/dist/markdown/to_markdown.js +413 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +5 -0
- package/dist/model/attrsCodec.js +48 -0
- package/dist/model/comparedeep.js +17 -0
- package/dist/model/content.d.ts +232 -0
- package/dist/model/content.js +630 -0
- package/dist/model/diff.js +66 -0
- package/dist/model/dom.d.ts +3 -0
- package/dist/model/errors.d.ts +90 -0
- package/dist/model/errors.js +76 -0
- package/dist/model/fragment.d.ts +131 -0
- package/dist/model/fragment.js +262 -0
- package/dist/model/from_dom.d.ts +299 -0
- package/dist/model/from_dom.js +618 -0
- package/dist/model/json.d.ts +37 -0
- package/dist/model/json.js +126 -0
- package/dist/model/mark.d.ts +73 -0
- package/dist/model/mark.js +121 -0
- package/dist/model/node.d.ts +267 -0
- package/dist/model/node.js +434 -0
- package/dist/model/replace.d.ts +78 -0
- package/dist/model/replace.js +207 -0
- package/dist/model/resolvedpos.d.ts +196 -0
- package/dist/model/resolvedpos.js +305 -0
- package/dist/model/schema.d.ts +740 -0
- package/dist/model/schema.js +451 -0
- package/dist/model/to_dom.d.ts +169 -0
- package/dist/model/to_dom.js +238 -0
- package/dist/model.d.ts +12 -0
- package/dist/model.js +12 -0
- package/dist/placeholder.d.ts +53 -0
- package/dist/placeholder.js +79 -0
- package/dist/reactive/observer.d.ts +54 -0
- package/dist/reactive/observer.js +61 -0
- package/dist/reactive/selectors.d.ts +22 -0
- package/dist/reactive/selectors.js +61 -0
- package/dist/reactive/stateref.d.ts +72 -0
- package/dist/reactive/stateref.js +71 -0
- package/dist/reactive/streams.d.ts +83 -0
- package/dist/reactive/streams.js +85 -0
- package/dist/reactive.d.ts +16 -0
- package/dist/reactive.js +26 -0
- package/dist/schema-basic.d.ts +448 -0
- package/dist/schema-basic.js +269 -0
- package/dist/schema-list/commands.d.ts +66 -0
- package/dist/schema-list/commands.js +203 -0
- package/dist/schema-list.d.ts +92 -0
- package/dist/schema-list.js +100 -0
- package/dist/state/errors.d.ts +39 -0
- package/dist/state/errors.js +32 -0
- package/dist/state/plugin.d.ts +232 -0
- package/dist/state/plugin.js +82 -0
- package/dist/state/selection.d.ts +297 -0
- package/dist/state/selection.js +486 -0
- package/dist/state/state.d.ts +212 -0
- package/dist/state/state.js +489 -0
- package/dist/state/transaction.d.ts +145 -0
- package/dist/state/transaction.js +195 -0
- package/dist/state.d.ts +6 -0
- package/dist/state.js +6 -0
- package/dist/test-builder.d.ts +3582 -0
- package/dist/test-builder.js +239 -0
- package/dist/transform/attr_step.d.ts +60 -0
- package/dist/transform/attr_step.js +105 -0
- package/dist/transform/errors.d.ts +50 -0
- package/dist/transform/errors.js +35 -0
- package/dist/transform/map.d.ts +182 -0
- package/dist/transform/map.js +280 -0
- package/dist/transform/mark.js +98 -0
- package/dist/transform/mark_set_step.js +69 -0
- package/dist/transform/mark_step.d.ts +113 -0
- package/dist/transform/mark_step.js +225 -0
- package/dist/transform/replace.d.ts +18 -0
- package/dist/transform/replace.js +381 -0
- package/dist/transform/replace_step.d.ts +109 -0
- package/dist/transform/replace_step.js +175 -0
- package/dist/transform/step.d.ts +157 -0
- package/dist/transform/step.js +168 -0
- package/dist/transform/structure.d.ts +89 -0
- package/dist/transform/structure.js +350 -0
- package/dist/transform/transform.d.ts +248 -0
- package/dist/transform/transform.js +373 -0
- package/dist/transform.d.ts +10 -0
- package/dist/transform.js +10 -0
- package/dist/view/browser.js +39 -0
- package/dist/view/capturekeys.js +282 -0
- package/dist/view/clipboard.d.ts +8 -0
- package/dist/view/clipboard.js +245 -0
- package/dist/view/decoration.d.ts +339 -0
- package/dist/view/decoration.js +566 -0
- package/dist/view/dom.d.ts +18 -0
- package/dist/view/dom.js +140 -0
- package/dist/view/domchange.js +295 -0
- package/dist/view/domcoords.d.ts +22 -0
- package/dist/view/domcoords.js +448 -0
- package/dist/view/domobserver.d.ts +47 -0
- package/dist/view/domobserver.js +328 -0
- package/dist/view/errors.d.ts +18 -0
- package/dist/view/errors.js +21 -0
- package/dist/view/input.d.ts +58 -0
- package/dist/view/input.js +704 -0
- package/dist/view/selection.js +198 -0
- package/dist/view/view.d.ts +699 -0
- package/dist/view/view.js +623 -0
- package/dist/view/viewdesc.d.ts +236 -0
- package/dist/view/viewdesc.js +1205 -0
- package/dist/view-css.d.ts +43 -0
- package/dist/view-css.js +124 -0
- package/dist/view.d.ts +6 -0
- package/dist/view.js +4 -0
- package/package.json +200 -4
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
import { attempt } from "../internal/attempt.js";
|
|
2
|
+
import { SchemaDefinitionError, isModelError } from "./errors.js";
|
|
3
|
+
import { Fragment } from "./fragment.js";
|
|
4
|
+
import { Mark } from "./mark.js";
|
|
5
|
+
import { Slice } from "./replace.js";
|
|
6
|
+
import * as Arr from "effect/Array";
|
|
7
|
+
import * as Option from "effect/Option";
|
|
8
|
+
// src/model/from_dom.ts
|
|
9
|
+
/**
|
|
10
|
+
* DOM parsing, ported from ProseMirror's `DOMParser`.
|
|
11
|
+
*
|
|
12
|
+
* @since 0.1.0
|
|
13
|
+
*/
|
|
14
|
+
function isTagRule(rule) {
|
|
15
|
+
return rule.tag != null;
|
|
16
|
+
}
|
|
17
|
+
function isStyleRule(rule) {
|
|
18
|
+
return rule.style != null;
|
|
19
|
+
}
|
|
20
|
+
const ruleOrigin = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
const originOf = (rule) => ruleOrigin.get(rule) ?? rule;
|
|
22
|
+
function withAttrs(rule, attrs) {
|
|
23
|
+
const result = {
|
|
24
|
+
...rule,
|
|
25
|
+
attrs: attrs || void 0
|
|
26
|
+
};
|
|
27
|
+
ruleOrigin.set(result, originOf(rule));
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A DOM parser represents a strategy for parsing DOM content into a
|
|
32
|
+
* document conforming to a given schema. Its behavior is defined by an
|
|
33
|
+
* array of [rules](#ParseRule).
|
|
34
|
+
*
|
|
35
|
+
* @since 0.1.0
|
|
36
|
+
* @category models
|
|
37
|
+
*/
|
|
38
|
+
var DOMParser = class DOMParser {
|
|
39
|
+
/**
|
|
40
|
+
* Create a parser that targets the given schema, using the given
|
|
41
|
+
* parsing rules.
|
|
42
|
+
*/
|
|
43
|
+
constructor(schema, rules) {
|
|
44
|
+
this.schema = schema;
|
|
45
|
+
this.rules = rules;
|
|
46
|
+
this.tags = [];
|
|
47
|
+
this.styles = [];
|
|
48
|
+
const matchedStyles = [];
|
|
49
|
+
const tags = [], styles = [];
|
|
50
|
+
rules.forEach((rule) => {
|
|
51
|
+
if (isTagRule(rule)) tags.push(rule);
|
|
52
|
+
else if (isStyleRule(rule)) {
|
|
53
|
+
const prop = /[^=]*/.exec(rule.style)[0];
|
|
54
|
+
if (matchedStyles.indexOf(prop) < 0) matchedStyles.push(prop);
|
|
55
|
+
styles.push(rule);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
this.tags = tags;
|
|
59
|
+
this.styles = styles;
|
|
60
|
+
this.matchedStyles = matchedStyles;
|
|
61
|
+
this.normalizeLists = !tags.some((r) => {
|
|
62
|
+
if (!/^(ul|ol)\b/.test(r.tag) || !r.node) return false;
|
|
63
|
+
const node = schema.nodes[r.node];
|
|
64
|
+
return !!node && !!node.contentMatch.matchTypeOrNull(node);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Parse a document from the content of a DOM node. */
|
|
68
|
+
parse(dom, options = {}) {
|
|
69
|
+
return attempt(() => this.parseUnsafe(dom, options), isModelError);
|
|
70
|
+
}
|
|
71
|
+
/** Like `parse`, but throws a `ModelError` on failure. */
|
|
72
|
+
parseUnsafe(dom, options = {}) {
|
|
73
|
+
const context = new ParseContext(this, options, false);
|
|
74
|
+
context.addAll(dom, Mark.none, options.from, options.to);
|
|
75
|
+
return context.finish();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Parses the content of the given DOM node, like
|
|
79
|
+
* [`parse`](#DOMParser.parse), and takes the same set of options.
|
|
80
|
+
* But unlike that method, which produces a whole node, this one
|
|
81
|
+
* returns a slice that is open at the sides, meaning that the schema
|
|
82
|
+
* constraints aren't applied to the start of nodes to the left of
|
|
83
|
+
* the input and the end of nodes at the end.
|
|
84
|
+
*/
|
|
85
|
+
parseSlice(dom, options = {}) {
|
|
86
|
+
return attempt(() => this.parseSliceUnsafe(dom, options), isModelError);
|
|
87
|
+
}
|
|
88
|
+
/** Like `parseSlice`, but throws a `ModelError` on failure. */
|
|
89
|
+
parseSliceUnsafe(dom, options = {}) {
|
|
90
|
+
const context = new ParseContext(this, options, true);
|
|
91
|
+
context.addAll(dom, Mark.none, options.from, options.to);
|
|
92
|
+
return Slice.maxOpen(context.finish());
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Find the first tag rule (after `after`, when given) that matches
|
|
96
|
+
* the given DOM node in the given context. When the rule has a
|
|
97
|
+
* `getAttrs` function, the returned rule is a copy whose `attrs` hold
|
|
98
|
+
* the computed attributes.
|
|
99
|
+
*/
|
|
100
|
+
matchTag(dom, context, after) {
|
|
101
|
+
return Option.fromNullishOr(this.matchTagOrNull(dom, context ?? new ParseContext(this, {}, false), after));
|
|
102
|
+
}
|
|
103
|
+
/** @internal */
|
|
104
|
+
matchTagOrNull(dom, context, after) {
|
|
105
|
+
for (let i = after ? this.tags.indexOf(originOf(after)) + 1 : 0; i < this.tags.length; i++) {
|
|
106
|
+
const rule = this.tags[i];
|
|
107
|
+
if (matches(dom, rule.tag) && (rule.namespace === void 0 || dom.namespaceURI == rule.namespace) && (!rule.context || context.matchesContext(rule.context))) {
|
|
108
|
+
if (rule.getAttrs) {
|
|
109
|
+
const result = rule.getAttrs(dom);
|
|
110
|
+
if (result === false) continue;
|
|
111
|
+
return withAttrs(rule, result);
|
|
112
|
+
}
|
|
113
|
+
return rule;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Find the first style rule (after `after`, when given) that matches
|
|
120
|
+
* the given style property and value in the given context.
|
|
121
|
+
*/
|
|
122
|
+
matchStyle(prop, value, context, after) {
|
|
123
|
+
return Option.fromNullishOr(this.matchStyleOrNull(prop, value, context ?? new ParseContext(this, {}, false), after));
|
|
124
|
+
}
|
|
125
|
+
/** @internal */
|
|
126
|
+
matchStyleOrNull(prop, value, context, after) {
|
|
127
|
+
for (let i = after ? this.styles.indexOf(originOf(after)) + 1 : 0; i < this.styles.length; i++) {
|
|
128
|
+
const rule = this.styles[i], style = rule.style;
|
|
129
|
+
if (style.indexOf(prop) != 0 || rule.context && !context.matchesContext(rule.context) || style.length > prop.length && (style.charCodeAt(prop.length) != 61 || style.slice(prop.length + 1) != value)) continue;
|
|
130
|
+
if (rule.getAttrs) {
|
|
131
|
+
const result = rule.getAttrs(value);
|
|
132
|
+
if (result === false) continue;
|
|
133
|
+
return withAttrs(rule, result);
|
|
134
|
+
}
|
|
135
|
+
return rule;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Gather the parse rules from a schema's node and mark specs,
|
|
141
|
+
* ordered by priority (and schema order for equal priorities). Rules
|
|
142
|
+
* without a `node`/`mark` get the name of the type they were defined
|
|
143
|
+
* on.
|
|
144
|
+
*/
|
|
145
|
+
static schemaRules(schema) {
|
|
146
|
+
const result = [];
|
|
147
|
+
function insert(rule) {
|
|
148
|
+
const priority = rule.priority == null ? 50 : rule.priority;
|
|
149
|
+
let i = 0;
|
|
150
|
+
for (; i < result.length; i++) {
|
|
151
|
+
const next = result[i];
|
|
152
|
+
if ((next.priority == null ? 50 : next.priority) < priority) break;
|
|
153
|
+
}
|
|
154
|
+
result.splice(i, 0, rule);
|
|
155
|
+
}
|
|
156
|
+
for (const name in schema.marks) {
|
|
157
|
+
const rules = schema.marks[name].spec.parseDOM;
|
|
158
|
+
if (rules) rules.forEach((r) => {
|
|
159
|
+
const rule = { ...r };
|
|
160
|
+
if (!(rule.mark || rule.ignore || rule.clearMark)) rule.mark = name;
|
|
161
|
+
insert(rule);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
for (const name in schema.nodes) {
|
|
165
|
+
const rules = schema.nodes[name].spec.parseDOM;
|
|
166
|
+
if (rules) rules.forEach((r) => {
|
|
167
|
+
const rule = { ...r };
|
|
168
|
+
if (!(rule.node || rule.ignore || rule.mark)) rule.node = name;
|
|
169
|
+
insert(rule);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Construct a DOM parser using the parsing rules listed in a schema's
|
|
176
|
+
* node specs, reordered by priority. The parser is cached on the
|
|
177
|
+
* schema.
|
|
178
|
+
*/
|
|
179
|
+
static fromSchema(schema) {
|
|
180
|
+
return schema.cached.domParser || (schema.cached.domParser = new DOMParser(schema, DOMParser.schemaRules(schema)));
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const blockTags = {
|
|
184
|
+
address: true,
|
|
185
|
+
article: true,
|
|
186
|
+
aside: true,
|
|
187
|
+
blockquote: true,
|
|
188
|
+
body: true,
|
|
189
|
+
canvas: true,
|
|
190
|
+
dd: true,
|
|
191
|
+
div: true,
|
|
192
|
+
dl: true,
|
|
193
|
+
fieldset: true,
|
|
194
|
+
figcaption: true,
|
|
195
|
+
figure: true,
|
|
196
|
+
footer: true,
|
|
197
|
+
form: true,
|
|
198
|
+
h1: true,
|
|
199
|
+
h2: true,
|
|
200
|
+
h3: true,
|
|
201
|
+
h4: true,
|
|
202
|
+
h5: true,
|
|
203
|
+
h6: true,
|
|
204
|
+
header: true,
|
|
205
|
+
hgroup: true,
|
|
206
|
+
hr: true,
|
|
207
|
+
li: true,
|
|
208
|
+
noscript: true,
|
|
209
|
+
ol: true,
|
|
210
|
+
output: true,
|
|
211
|
+
p: true,
|
|
212
|
+
pre: true,
|
|
213
|
+
section: true,
|
|
214
|
+
table: true,
|
|
215
|
+
tfoot: true,
|
|
216
|
+
ul: true
|
|
217
|
+
};
|
|
218
|
+
const ignoreTags = {
|
|
219
|
+
head: true,
|
|
220
|
+
noscript: true,
|
|
221
|
+
object: true,
|
|
222
|
+
script: true,
|
|
223
|
+
style: true,
|
|
224
|
+
title: true
|
|
225
|
+
};
|
|
226
|
+
const listTags = {
|
|
227
|
+
ol: true,
|
|
228
|
+
ul: true
|
|
229
|
+
};
|
|
230
|
+
const hasOwn = (table, key) => Object.prototype.hasOwnProperty.call(table, key);
|
|
231
|
+
const OPT_PRESERVE_WS = 1;
|
|
232
|
+
const OPT_PRESERVE_WS_FULL = 2;
|
|
233
|
+
const OPT_OPEN_LEFT = 4;
|
|
234
|
+
function wsOptionsFor(type, preserveWhitespace, base) {
|
|
235
|
+
if (preserveWhitespace != null) return (preserveWhitespace ? OPT_PRESERVE_WS : 0) | (preserveWhitespace === "full" ? OPT_PRESERVE_WS_FULL : 0);
|
|
236
|
+
return type && type.whitespace == "pre" ? 3 : base & -5;
|
|
237
|
+
}
|
|
238
|
+
var NodeContext = class {
|
|
239
|
+
constructor(type, attrs, marks, solid, match, options) {
|
|
240
|
+
this.type = type;
|
|
241
|
+
this.attrs = attrs;
|
|
242
|
+
this.marks = marks;
|
|
243
|
+
this.solid = solid;
|
|
244
|
+
this.options = options;
|
|
245
|
+
this.content = [];
|
|
246
|
+
this.activeMarks = Mark.none;
|
|
247
|
+
this.match = match || (options & OPT_OPEN_LEFT ? null : type.contentMatch);
|
|
248
|
+
}
|
|
249
|
+
findWrapping(node) {
|
|
250
|
+
if (!this.match) {
|
|
251
|
+
if (!this.type) return [];
|
|
252
|
+
const fill = this.type.contentMatch.fillBeforeOrNull(Fragment.from(node));
|
|
253
|
+
if (fill) this.match = this.type.contentMatch.matchFragmentOrNull(fill);
|
|
254
|
+
else {
|
|
255
|
+
const start = this.type.contentMatch;
|
|
256
|
+
const wrap = start.findWrappingOrNull(node.type);
|
|
257
|
+
if (wrap) {
|
|
258
|
+
this.match = start;
|
|
259
|
+
return wrap;
|
|
260
|
+
} else return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return this.match.findWrappingOrNull(node.type);
|
|
264
|
+
}
|
|
265
|
+
finish(openEnd) {
|
|
266
|
+
if (!(this.options & OPT_PRESERVE_WS)) {
|
|
267
|
+
const last = this.content[this.content.length - 1];
|
|
268
|
+
let m;
|
|
269
|
+
if (last && last.isText && (m = /[ \t\r\n\f]+$/.exec(last.text))) {
|
|
270
|
+
const text = last;
|
|
271
|
+
if (text.text.length == m[0].length) this.content.pop();
|
|
272
|
+
else this.content[this.content.length - 1] = text.withText(text.text.slice(0, text.text.length - m[0].length));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
let content = Fragment.from(this.content);
|
|
276
|
+
if (!openEnd && this.match) content = content.append(this.match.fillBeforeOrNull(Fragment.empty, true));
|
|
277
|
+
return this.type ? this.type.createUnsafe(this.attrs, content, this.marks) : content;
|
|
278
|
+
}
|
|
279
|
+
inlineContext(node) {
|
|
280
|
+
if (this.type) return this.type.inlineContent;
|
|
281
|
+
if (this.content.length) return this.content[0].isInline;
|
|
282
|
+
return !!node.parentNode && !hasOwn(blockTags, node.parentNode.nodeName.toLowerCase());
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
/** @internal */
|
|
286
|
+
var ParseContext = class {
|
|
287
|
+
constructor(parser, options, isOpen) {
|
|
288
|
+
this.parser = parser;
|
|
289
|
+
this.options = options;
|
|
290
|
+
this.isOpen = isOpen;
|
|
291
|
+
this.open = 0;
|
|
292
|
+
this.localPreserveWS = false;
|
|
293
|
+
const topNode = options.topNode;
|
|
294
|
+
let topContext;
|
|
295
|
+
const topOptions = wsOptionsFor(null, options.preserveWhitespace, 0) | (isOpen ? OPT_OPEN_LEFT : 0);
|
|
296
|
+
if (topNode) topContext = new NodeContext(topNode.type, topNode.attrs, Mark.none, true, options.topMatch || topNode.type.contentMatch, topOptions);
|
|
297
|
+
else if (isOpen) topContext = new NodeContext(null, null, Mark.none, true, null, topOptions);
|
|
298
|
+
else topContext = new NodeContext(parser.schema.topNodeType, null, Mark.none, true, null, topOptions);
|
|
299
|
+
this.nodes = [topContext];
|
|
300
|
+
this.find = options.findPositions;
|
|
301
|
+
this.needsBlock = false;
|
|
302
|
+
}
|
|
303
|
+
get top() {
|
|
304
|
+
return this.nodes[this.open];
|
|
305
|
+
}
|
|
306
|
+
addDOM(dom, marks) {
|
|
307
|
+
if (dom.nodeType == 3) this.addTextNode(dom, marks);
|
|
308
|
+
else if (dom.nodeType == 1) this.addElement(dom, marks);
|
|
309
|
+
}
|
|
310
|
+
addTextNode(dom, marks) {
|
|
311
|
+
let value = dom.nodeValue;
|
|
312
|
+
const top = this.top;
|
|
313
|
+
const preserveWS = top.options & OPT_PRESERVE_WS_FULL ? "full" : this.localPreserveWS || (top.options & OPT_PRESERVE_WS) > 0;
|
|
314
|
+
const schema = this.parser.schema;
|
|
315
|
+
const linebreak = Option.getOrNull(schema.linebreakReplacement);
|
|
316
|
+
if (preserveWS === "full" || top.inlineContext(dom) || /[^ \t\r\n\f]/.test(value)) {
|
|
317
|
+
if (!preserveWS) {
|
|
318
|
+
value = value.replace(/[ \t\r\n\f]+/g, " ");
|
|
319
|
+
if (/^[ \t\r\n\f]/.test(value) && this.open == this.nodes.length - 1) {
|
|
320
|
+
const nodeBefore = top.content[top.content.length - 1];
|
|
321
|
+
const domNodeBefore = dom.previousSibling;
|
|
322
|
+
if (!nodeBefore || domNodeBefore && domNodeBefore.nodeName == "BR" || nodeBefore.isText && /[ \t\r\n\f]$/.test(nodeBefore.text)) value = value.slice(1);
|
|
323
|
+
}
|
|
324
|
+
} else if (preserveWS === "full") value = value.replace(/\r\n?/g, "\n");
|
|
325
|
+
else if (linebreak && /[\r\n]/.test(value) && this.top.findWrapping(linebreak.createUnsafe())) {
|
|
326
|
+
const lines = value.split(/\r?\n|\r/);
|
|
327
|
+
for (let i = 0; i < lines.length; i++) {
|
|
328
|
+
if (i) this.insertNode(linebreak.createUnsafe(), marks, true);
|
|
329
|
+
if (lines[i]) this.insertNode(schema.textUnsafe(lines[i]), marks, !/\S/.test(lines[i]));
|
|
330
|
+
}
|
|
331
|
+
value = "";
|
|
332
|
+
} else value = value.replace(/\r?\n|\r/g, " ");
|
|
333
|
+
if (value) this.insertNode(schema.textUnsafe(value), marks, !/\S/.test(value));
|
|
334
|
+
this.findInText(dom);
|
|
335
|
+
} else this.findInside(dom);
|
|
336
|
+
}
|
|
337
|
+
addElement(dom, marks, matchAfter) {
|
|
338
|
+
const outerWS = this.localPreserveWS;
|
|
339
|
+
let top = this.top;
|
|
340
|
+
if (dom.tagName == "PRE" || /pre/.test(dom.style && dom.style.whiteSpace)) this.localPreserveWS = true;
|
|
341
|
+
const name = dom.nodeName.toLowerCase();
|
|
342
|
+
let ruleID;
|
|
343
|
+
if (hasOwn(listTags, name) && this.parser.normalizeLists) normalizeList(dom);
|
|
344
|
+
const rule = this.options.ruleFromNode && this.options.ruleFromNode(dom) || (ruleID = this.parser.matchTagOrNull(dom, this, matchAfter));
|
|
345
|
+
out: if (rule ? rule.ignore : hasOwn(ignoreTags, name)) {
|
|
346
|
+
this.findInside(dom);
|
|
347
|
+
this.ignoreFallback(dom, marks);
|
|
348
|
+
} else if (!rule || rule.skip || rule.closeParent) {
|
|
349
|
+
if (rule && rule.closeParent) this.open = Math.max(0, this.open - 1);
|
|
350
|
+
else if (rule && rule.skip.nodeType) dom = rule.skip;
|
|
351
|
+
let sync = false;
|
|
352
|
+
const oldNeedsBlock = this.needsBlock;
|
|
353
|
+
if (hasOwn(blockTags, name)) {
|
|
354
|
+
if (top.content.length && top.content[0].isInline && this.open) {
|
|
355
|
+
this.open--;
|
|
356
|
+
top = this.top;
|
|
357
|
+
}
|
|
358
|
+
sync = true;
|
|
359
|
+
if (!top.type) this.needsBlock = true;
|
|
360
|
+
} else if (!dom.firstChild) {
|
|
361
|
+
this.leafFallback(dom, marks);
|
|
362
|
+
break out;
|
|
363
|
+
}
|
|
364
|
+
const innerMarks = rule && rule.skip ? marks : this.readStyles(dom, marks);
|
|
365
|
+
if (innerMarks) this.addAll(dom, innerMarks);
|
|
366
|
+
if (sync) this.sync(top);
|
|
367
|
+
this.needsBlock = oldNeedsBlock;
|
|
368
|
+
} else {
|
|
369
|
+
const innerMarks = this.readStyles(dom, marks);
|
|
370
|
+
if (innerMarks) this.addElementByRule(dom, rule, innerMarks, rule.consuming === false ? ruleID ?? void 0 : void 0);
|
|
371
|
+
}
|
|
372
|
+
this.localPreserveWS = outerWS;
|
|
373
|
+
}
|
|
374
|
+
leafFallback(dom, marks) {
|
|
375
|
+
if (dom.nodeName == "BR" && this.top.type && this.top.type.inlineContent) this.addTextNode(dom.ownerDocument.createTextNode("\n"), marks);
|
|
376
|
+
}
|
|
377
|
+
ignoreFallback(dom, marks) {
|
|
378
|
+
if (dom.nodeName == "BR" && (!this.top.type || !this.top.type.inlineContent)) this.findPlace(this.parser.schema.textUnsafe("-"), marks, true);
|
|
379
|
+
}
|
|
380
|
+
readStyles(dom, marks) {
|
|
381
|
+
const styles = dom.style;
|
|
382
|
+
if (styles && styles.length) for (let i = 0; i < this.parser.matchedStyles.length; i++) {
|
|
383
|
+
const name = this.parser.matchedStyles[i], value = styles.getPropertyValue(name);
|
|
384
|
+
if (value) for (let after = void 0;;) {
|
|
385
|
+
const rule = this.parser.matchStyleOrNull(name, value, this, after);
|
|
386
|
+
if (!rule) break;
|
|
387
|
+
if (rule.ignore) return null;
|
|
388
|
+
if (rule.clearMark) marks = marks.filter((m) => !rule.clearMark(m));
|
|
389
|
+
else marks = marks.concat(markType(this.parser.schema, rule.mark).createUnsafe(rule.attrs));
|
|
390
|
+
if (rule.consuming === false) after = rule;
|
|
391
|
+
else break;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return marks;
|
|
395
|
+
}
|
|
396
|
+
addElementByRule(dom, rule, marks, continueAfter) {
|
|
397
|
+
let sync = false, nodeType;
|
|
398
|
+
if (rule.node) {
|
|
399
|
+
nodeType = this.parser.schema.nodeType(rule.node);
|
|
400
|
+
if (!nodeType.isLeaf) {
|
|
401
|
+
const inner = this.enter(nodeType, rule.attrs || null, marks, rule.preserveWhitespace);
|
|
402
|
+
if (inner) {
|
|
403
|
+
sync = true;
|
|
404
|
+
marks = inner;
|
|
405
|
+
}
|
|
406
|
+
} else if (!this.insertNode(nodeType.createUnsafe(rule.attrs), marks, dom.nodeName == "BR")) this.leafFallback(dom, marks);
|
|
407
|
+
} else marks = marks.concat(markType(this.parser.schema, rule.mark).createUnsafe(rule.attrs));
|
|
408
|
+
const startIn = this.top;
|
|
409
|
+
if (nodeType && nodeType.isLeaf) this.findInside(dom);
|
|
410
|
+
else if (continueAfter) this.addElement(dom, marks, continueAfter);
|
|
411
|
+
else if (rule.getContent) {
|
|
412
|
+
this.findInside(dom);
|
|
413
|
+
rule.getContent(dom, this.parser.schema).forEach((node) => this.insertNode(node, marks, false));
|
|
414
|
+
} else {
|
|
415
|
+
let contentDOM = dom;
|
|
416
|
+
if (typeof rule.contentElement == "string") contentDOM = dom.querySelector(rule.contentElement);
|
|
417
|
+
else if (typeof rule.contentElement == "function") contentDOM = rule.contentElement(dom);
|
|
418
|
+
else if (rule.contentElement) contentDOM = rule.contentElement;
|
|
419
|
+
this.findAround(dom, contentDOM, true);
|
|
420
|
+
this.addAll(contentDOM, marks);
|
|
421
|
+
this.findAround(dom, contentDOM, false);
|
|
422
|
+
}
|
|
423
|
+
if (sync && this.sync(startIn)) this.open--;
|
|
424
|
+
}
|
|
425
|
+
addAll(parent, marks, startIndex, endIndex) {
|
|
426
|
+
let index = startIndex || 0;
|
|
427
|
+
for (let dom = startIndex ? parent.childNodes[startIndex] : parent.firstChild, end = endIndex == null ? null : parent.childNodes[endIndex]; dom != end; dom = dom.nextSibling, ++index) {
|
|
428
|
+
this.findAtPoint(parent, index);
|
|
429
|
+
this.addDOM(dom, marks);
|
|
430
|
+
}
|
|
431
|
+
this.findAtPoint(parent, index);
|
|
432
|
+
}
|
|
433
|
+
findPlace(node, marks, cautious) {
|
|
434
|
+
let route, sync;
|
|
435
|
+
for (let depth = this.open, penalty = 0; depth >= 0; depth--) {
|
|
436
|
+
const cx = this.nodes[depth];
|
|
437
|
+
const found = cx.findWrapping(node);
|
|
438
|
+
if (found && (!route || route.length > found.length + penalty)) {
|
|
439
|
+
route = found;
|
|
440
|
+
sync = cx;
|
|
441
|
+
if (!found.length) break;
|
|
442
|
+
}
|
|
443
|
+
if (cx.solid) {
|
|
444
|
+
if (cautious) break;
|
|
445
|
+
penalty += 2;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
if (!route) return null;
|
|
449
|
+
this.sync(sync);
|
|
450
|
+
for (let i = 0; i < route.length; i++) marks = this.enterInner(route[i], null, marks, false);
|
|
451
|
+
return marks;
|
|
452
|
+
}
|
|
453
|
+
insertNode(node, marks, cautious) {
|
|
454
|
+
if (node.isInline && this.needsBlock && !this.top.type) {
|
|
455
|
+
const block = this.textblockFromContext();
|
|
456
|
+
if (block) marks = this.enterInner(block, null, marks);
|
|
457
|
+
}
|
|
458
|
+
const innerMarks = this.findPlace(node, marks, cautious);
|
|
459
|
+
if (innerMarks) {
|
|
460
|
+
this.closeExtra();
|
|
461
|
+
const top = this.top;
|
|
462
|
+
if (top.match) top.match = top.match.matchTypeOrNull(node.type);
|
|
463
|
+
let nodeMarks = Mark.none;
|
|
464
|
+
for (const m of innerMarks.concat(node.marks)) if (top.type ? top.type.allowsMarkType(m.type) : markMayApply(m.type, node.type)) nodeMarks = m.addToSet(nodeMarks);
|
|
465
|
+
top.content.push(node.mark(nodeMarks));
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
enter(type, attrs, marks, preserveWS) {
|
|
471
|
+
let innerMarks = this.findPlace(type.createUnsafe(attrs), marks, false);
|
|
472
|
+
if (innerMarks) innerMarks = this.enterInner(type, attrs, marks, true, preserveWS);
|
|
473
|
+
return innerMarks;
|
|
474
|
+
}
|
|
475
|
+
enterInner(type, attrs, marks, solid = false, preserveWS) {
|
|
476
|
+
this.closeExtra();
|
|
477
|
+
const top = this.top;
|
|
478
|
+
top.match = top.match && top.match.matchTypeOrNull(type);
|
|
479
|
+
let options = wsOptionsFor(type, preserveWS, top.options);
|
|
480
|
+
if (top.options & OPT_OPEN_LEFT && Arr.isReadonlyArrayEmpty(top.content)) options |= OPT_OPEN_LEFT;
|
|
481
|
+
let applyMarks = Mark.none;
|
|
482
|
+
marks = marks.filter((m) => {
|
|
483
|
+
if (top.type ? top.type.allowsMarkType(m.type) : markMayApply(m.type, type)) {
|
|
484
|
+
applyMarks = m.addToSet(applyMarks);
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
});
|
|
489
|
+
this.nodes.push(new NodeContext(type, attrs, applyMarks, solid, null, options));
|
|
490
|
+
this.open++;
|
|
491
|
+
return marks;
|
|
492
|
+
}
|
|
493
|
+
closeExtra(openEnd = false) {
|
|
494
|
+
let i = this.nodes.length - 1;
|
|
495
|
+
if (i > this.open) {
|
|
496
|
+
for (; i > this.open; i--) this.nodes[i - 1].content.push(this.nodes[i].finish(openEnd));
|
|
497
|
+
this.nodes.length = this.open + 1;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
finish() {
|
|
501
|
+
this.open = 0;
|
|
502
|
+
this.closeExtra(this.isOpen);
|
|
503
|
+
return this.nodes[0].finish(!!(this.isOpen || this.options.topOpen));
|
|
504
|
+
}
|
|
505
|
+
sync(to) {
|
|
506
|
+
for (let i = this.open; i >= 0; i--) if (this.nodes[i] == to) {
|
|
507
|
+
this.open = i;
|
|
508
|
+
return true;
|
|
509
|
+
} else if (this.localPreserveWS) this.nodes[i].options |= OPT_PRESERVE_WS;
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
get currentPos() {
|
|
513
|
+
this.closeExtra();
|
|
514
|
+
let pos = 0;
|
|
515
|
+
for (let i = this.open; i >= 0; i--) {
|
|
516
|
+
const content = this.nodes[i].content;
|
|
517
|
+
for (let j = content.length - 1; j >= 0; j--) pos += content[j].nodeSize;
|
|
518
|
+
if (i) pos++;
|
|
519
|
+
}
|
|
520
|
+
return pos;
|
|
521
|
+
}
|
|
522
|
+
findAtPoint(parent, offset) {
|
|
523
|
+
if (this.find) {
|
|
524
|
+
for (let i = 0; i < this.find.length; i++) if (this.find[i].node == parent && this.find[i].offset == offset) this.find[i].pos = this.currentPos;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
findInside(parent) {
|
|
528
|
+
if (this.find) {
|
|
529
|
+
for (let i = 0; i < this.find.length; i++) if (this.find[i].pos == null && parent.nodeType == 1 && parent.contains(this.find[i].node)) this.find[i].pos = this.currentPos;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
findAround(parent, content, before) {
|
|
533
|
+
if (parent != content && this.find) {
|
|
534
|
+
for (let i = 0; i < this.find.length; i++) if (this.find[i].pos == null && parent.nodeType == 1 && parent.contains(this.find[i].node)) {
|
|
535
|
+
if (content.compareDocumentPosition(this.find[i].node) & (before ? 2 : 4)) this.find[i].pos = this.currentPos;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
findInText(textNode) {
|
|
540
|
+
if (this.find) {
|
|
541
|
+
for (let i = 0; i < this.find.length; i++) if (this.find[i].node == textNode) this.find[i].pos = this.currentPos - (textNode.nodeValue.length - this.find[i].offset);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
matchesContext(context) {
|
|
545
|
+
if (context.indexOf("|") > -1) return context.split(/\s*\|\s*/).some(this.matchesContext, this);
|
|
546
|
+
const parts = context.split("/");
|
|
547
|
+
const option = this.options.context;
|
|
548
|
+
const useRoot = !this.isOpen && (!option || option.parent.type == this.nodes[0].type);
|
|
549
|
+
const minDepth = -(option ? option.depth + 1 : 0) + (useRoot ? 0 : 1);
|
|
550
|
+
const match = (i, depth) => {
|
|
551
|
+
for (; i >= 0; i--) {
|
|
552
|
+
const part = parts[i];
|
|
553
|
+
if (part == "") {
|
|
554
|
+
if (i == parts.length - 1 || i == 0) continue;
|
|
555
|
+
for (; depth >= minDepth; depth--) if (match(i - 1, depth)) return true;
|
|
556
|
+
return false;
|
|
557
|
+
} else {
|
|
558
|
+
const next = depth > 0 || depth == 0 && useRoot ? this.nodes[depth].type : option && depth >= minDepth ? option.node(depth - minDepth).type : null;
|
|
559
|
+
if (!next || next.name != part && !next.isInGroup(part)) return false;
|
|
560
|
+
depth--;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
};
|
|
565
|
+
return match(parts.length - 1, this.open);
|
|
566
|
+
}
|
|
567
|
+
textblockFromContext() {
|
|
568
|
+
const $context = this.options.context;
|
|
569
|
+
if ($context) for (let d = $context.depth; d >= 0; d--) {
|
|
570
|
+
const deflt = $context.node(d).contentMatchAtUnsafe($context.indexAfter(d)).defaultTypeOrNull;
|
|
571
|
+
if (deflt && deflt.isTextblock && deflt.defaultAttrs) return deflt;
|
|
572
|
+
}
|
|
573
|
+
const nodes = this.parser.schema.nodes;
|
|
574
|
+
for (const name in nodes) {
|
|
575
|
+
const type = nodes[name];
|
|
576
|
+
if (type.isTextblock && type.defaultAttrs) return type;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
function markType(schema, name) {
|
|
581
|
+
const type = name === void 0 ? void 0 : schema.marks[name];
|
|
582
|
+
if (!type) throw new SchemaDefinitionError({ message: `Parse rule refers to unknown mark type ${String(name)}` });
|
|
583
|
+
return type;
|
|
584
|
+
}
|
|
585
|
+
function normalizeList(dom) {
|
|
586
|
+
for (let child = dom.firstChild, prevItem = null; child; child = child.nextSibling) {
|
|
587
|
+
const name = child.nodeType == 1 ? child.nodeName.toLowerCase() : null;
|
|
588
|
+
if (name && hasOwn(listTags, name) && prevItem) {
|
|
589
|
+
prevItem.appendChild(child);
|
|
590
|
+
child = prevItem;
|
|
591
|
+
} else if (name == "li") prevItem = child;
|
|
592
|
+
else if (name) prevItem = null;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function matches(dom, selector) {
|
|
596
|
+
const f = dom.matches || dom.msMatchesSelector || dom.webkitMatchesSelector || dom.mozMatchesSelector;
|
|
597
|
+
return !!f && f.call(dom, selector);
|
|
598
|
+
}
|
|
599
|
+
function markMayApply(markType, nodeType) {
|
|
600
|
+
const nodes = nodeType.schema.nodes;
|
|
601
|
+
for (const name in nodes) {
|
|
602
|
+
const parent = nodes[name];
|
|
603
|
+
if (!parent.allowsMarkType(markType)) continue;
|
|
604
|
+
const seen = [];
|
|
605
|
+
const scan = (match) => {
|
|
606
|
+
seen.push(match);
|
|
607
|
+
for (let i = 0; i < match.next.length; i++) {
|
|
608
|
+
const { next, type } = match.next[i];
|
|
609
|
+
if (type == nodeType) return true;
|
|
610
|
+
if (seen.indexOf(next) < 0 && scan(next)) return true;
|
|
611
|
+
}
|
|
612
|
+
return false;
|
|
613
|
+
};
|
|
614
|
+
if (scan(parent.contentMatch)) return true;
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
export { DOMParser };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Fragment } from "./fragment.js";
|
|
2
|
+
import { Mark } from "./mark.js";
|
|
3
|
+
import { Node, NodeJSON } from "./node.js";
|
|
4
|
+
import { Slice } from "./replace.js";
|
|
5
|
+
import { AnySchema } from "./schema.js";
|
|
6
|
+
import * as S from "effect/Schema";
|
|
7
|
+
/** @internal */
|
|
8
|
+
export declare function markFromJSON<Sc extends AnySchema>(schema: Sc, json: any): Mark<Sc>;
|
|
9
|
+
/** @internal */
|
|
10
|
+
export declare function nodeFromJSON<Sc extends AnySchema>(schema: Sc, json: any): Node<Sc>;
|
|
11
|
+
/** @internal */
|
|
12
|
+
export declare function fragmentFromJSON<Sc extends AnySchema>(schema: Sc, value: any): Fragment<Sc>;
|
|
13
|
+
/** @internal */
|
|
14
|
+
export declare function sliceFromJSON<Sc extends AnySchema>(schema: Sc, value: any): Slice<Sc>;
|
|
15
|
+
/**
|
|
16
|
+
* An Effect `Schema` that decodes the JSON representation of a node
|
|
17
|
+
* into a `Node` of the given document schema (validating structure,
|
|
18
|
+
* content, and attributes), and encodes nodes back to JSON.
|
|
19
|
+
*
|
|
20
|
+
* ```ts skip-type-checking
|
|
21
|
+
* import { Schema } from "effect"
|
|
22
|
+
* const doc = yield* Schema.decodeUnknownEffect(NodeFromJSON(mySchema))(json)
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @since 0.1.0
|
|
26
|
+
* @category schemas
|
|
27
|
+
*/
|
|
28
|
+
export declare const NodeFromJSON: <Sc extends AnySchema>(schema: Sc) => S.Codec<Node<Sc>, NodeJSON>;
|
|
29
|
+
/**
|
|
30
|
+
* An Effect `Schema` for the top-level document of a schema: like
|
|
31
|
+
* `NodeFromJSON`, but also requires the decoded node to be of the
|
|
32
|
+
* schema's top node type.
|
|
33
|
+
*
|
|
34
|
+
* @since 0.1.0
|
|
35
|
+
* @category schemas
|
|
36
|
+
*/
|
|
37
|
+
export declare const DocFromJSON: <Sc extends AnySchema>(schema: Sc) => S.Codec<Node<Sc>, NodeJSON>;
|