pax_global_header00006660000000000000000000000064152542254450014522gustar00rootroot0000000000000052 comment=4211aeeaa3d738454a5d1428a95c1a210e86d787 prosemirror-model/000077500000000000000000000000001525422544500145475ustar00rootroot00000000000000prosemirror-model/.gitignore000066400000000000000000000000521525422544500165340ustar00rootroot00000000000000/node_modules .tern-port /dist /test/*.js prosemirror-model/.npmignore000066400000000000000000000000371525422544500165460ustar00rootroot00000000000000/node_modules .tern-port /test prosemirror-model/.npmrc000066400000000000000000000000231525422544500156620ustar00rootroot00000000000000package-lock=false prosemirror-model/.tern-project000066400000000000000000000001571525422544500171670ustar00rootroot00000000000000{ "libs": ["browser"], "plugins": { "node": {}, "complete_strings": {}, "es_modules": {} } } prosemirror-model/CHANGELOG.md000066400000000000000000000715451525422544500163740ustar00rootroot00000000000000## 1.25.12 (2026-09-21) ### Bug fixes Perform attribute validation in `NodeType`/`MarkType.create` to make it easier to ensure it happens for every way nodes and marks are created. ## 1.25.11 (2026-07-11) ### Bug fixes Fix parsing of trailing/leading whitespace when the element given to parseSlice is a body element. ## 1.25.10 (2026-07-06) ### Bug fixes Allow non-HTML elements in `DOMOutputSpec`. ## 1.25.9 (2026-06-15) ### Bug fixes Fix a bug that caused some types of invalid replace steps to crash, rather than returning a failed `StepResult`, on apply. ## 1.25.8 (2026-06-10) ### Bug fixes Fix bad exception message caused by a shadowed variable. Make sure `Fragment.findDiffStart`/`End` doesn't return positions in the middle of UTF16 surrogate pairs. ## 1.25.7 (2026-05-18) ### Bug fixes Fix a regression (since 1.25.5) in the way `DOMOutputSpec`s are interpreted. ## 1.25.6 (2026-05-15) ### Bug fixes Remove an accidentally commited `console.log`. ## 1.25.5 (2026-05-14) ### Bug fixes Properly reflect the fact that nodes must not serialize to just text nodes in the `DOMOutputSpec` type. Don't check content constraints in `Slice.insertAt` when inserting into an open node. ## 1.25.4 (2025-10-21) ### Bug fixes The DOM parser will now, if a line break replacement is defined in the schema, try to use that instead of spaces to replace newlines when preserving whitespace. ## 1.25.3 (2025-08-06) ### Bug fixes Fix a bug in `Slice` that made it possible for invalid `ReplaceAroundStep`s to be applied in some situations. ## 1.25.2 (2025-07-11) ### Bug fixes Suppress lint warnings about dereferencing methods by making `Schema.nodeFromJSON` and `markFromJSON` properties instead of methods. Avoid using `setAttribute("style", ...)` to stay clear of content security policies. ## 1.25.1 (2025-04-22) ### Bug fixes Make the DOM parser not discard nodes whose document representation cannot be placed inside the represenation of some parent DOM node. ## 1.25.0 (2025-03-18) ### New features Mark specs can now be marked with a `code` property. ## 1.24.1 (2024-12-10) ### Bug fixes The parser now automatically preserves whitespace inside `
` or `white-space: pre` elements.

## 1.24.0 (2024-11-27)

### New features

`Fragment.content` and `Node.children` now expose a node's set of children as an array.

## 1.23.0 (2024-10-05)

### New features

The new `NodeType.isInGroup` method can be used to query group membership.

## 1.22.3 (2024-08-06)

### Bug fixes

Fix some corner cases in the way the DOM parser tracks active marks.

## 1.22.2 (2024-07-18)

### Bug fixes

Make attribute validation messages more informative.

## 1.22.1 (2024-07-14)

### Bug fixes

Add code to `DOMSerializer` that rejects DOM output specs when they originate from attribute values, to protect against XSS attacks that use corrupt attribute input.

## 1.22.0 (2024-07-14)

### New features

Attribute specs now support a `validate` property that can be used to provide a validation function for the attribute, to guard against corrupt JSON input.

## 1.21.3 (2024-06-26)

### Bug fixes

Fix an issue where parse rules for CSS properties that were shorthands for a number of more detailed properties weren't matching properly.

## 1.21.2 (2024-06-25)

### Bug fixes

Make sure resolved positions (and thus the document and schema hanging off them) don't get kept in the cache when their document can be garbage-collected.

## 1.21.1 (2024-06-03)

### Bug fixes

Improve performance and accuracy of `DOMParser` style matching by using the DOM's own `style` object.

## 1.21.0 (2024-05-06)

### New features

The new `linebreakReplacement` property on node specs makes it possible to configure a node type that `setBlockType` will convert to and from line breaks when appropriate.

## 1.20.0 (2024-04-08)

### New features

The `ParseRule` type is now a union of `TagParseRule` and `StyleParseRule`, with more specific types being used when appropriate.

## 1.19.4 (2023-12-11)

### Bug fixes

Make `textBetween` emit block separators for empty textblocks.

## 1.19.3 (2023-07-13)

### Bug fixes

Don't apply style parse rules for nodes that are skipped by other parse rules.

## 1.19.2 (2023-05-23)

### Bug fixes

Allow parse rules with a `clearMark` directive to clear marks that have already been applied.

## 1.19.1 (2023-05-17)

### Bug fixes

Fix the types of `Fragment.desendants` to include the index parameter to the callback. Add release note

Include CommonJS type declarations in the package to please new TypeScript resolution settings.

## 1.19.0 (2023-01-18)

### New features

Parse rules for styles can now provide a `clearMark` property to remove pending marks (for example for `font-style: normal`).

## 1.18.3 (2022-11-18)

### Bug fixes

Copy all properties from the input spec to `Schema.spec`.

## 1.18.2 (2022-11-14)

### Bug fixes

Improve DOM parsing of nested block elements mixing block and inline children.

## 1.18.1 (2022-06-15)

### Bug fixes

Upgrade to orderedmap 2.0.0 to avoid around a TypeScript compilation issue.

## 1.18.0 (2022-06-07)

### New features

Node specs for leaf nodes now support a property `leafText` which, when given, will be used by `textContent` and `textBetween` to serialize the node.

Add optional type parameters to `Schema` for the node and mark names. Clarify Schema type parameters

## 1.17.0 (2022-05-30)

### Bug fixes

Fix a crash in DOM parsing.

### New features

Include TypeScript type declarations.

## 1.16.1 (2021-12-29)

### Bug fixes

Fix a bug in the way whitespace-preservation options were handled in `DOMParser`.

## 1.16.0 (2021-12-27)

### New features

A new `NodeSpec` property, `whitespace`, allows more control over the way whitespace in the content of the node is parsed.

## 1.15.0 (2021-10-25)

### New features

`textBetween` now allows its leaf text argument to be a function.

## 1.14.3 (2021-07-22)

### Bug fixes

`DOMSerializer.serializeNode` will no longer ignore the node's marks.

## 1.14.2 (2021-06-16)

### Bug fixes

Be less agressive about dropping whitespace when the context isn't know in `DOMParser.parseSlice`.

## 1.14.1 (2021-04-26)

### Bug fixes

DOM parsing with `preserveWhitespace: "full"` will no longer ignore whitespace-only nodes.

## 1.14.0 (2021-04-06)

### Bug fixes

`Node.check` will now error if a node has an invalid combination of marks.

Don't leave carriage return characters in parsed DOM content, since they confuse Chrome's cursor motion.

### New features

`Fragment.textBetween` is now public.

## 1.13.3 (2021-02-04)

### Bug fixes

Fix an issue where nested tags that match mark parser rules could cause the parser to apply marks in invalid places.

## 1.13.2 (2021-02-04)

### Bug fixes

`MarkType.removeFromSet` now removes all instances of the mark, not just the first one.

## 1.13.1 (2020-12-20)

### Bug fixes

Fix a bug where nested marks of the same type would be applied to the wrong node when parsing from DOM.

## 1.13.0 (2020-12-11)

### New features

Parse rules can now have a `consuming: false` property which allows other rules to match their tag or style even when they apply.

## 1.12.0 (2020-10-11)

### New features

The output of `toDOM` functions can now be a `{dom, contentDOM}` object specifying the precise parent and content DOM elements.

## 1.11.2 (2020-09-12)

### Bug fixes

Fix issue where 1.11.1 uses an array method not available on Internet Explorer.

## 1.11.1 (2020-09-11)

### Bug fixes

Fix an issue where an inner node's mark information could reset the same mark provided by an outer node in the DOM parser.

## 1.11.0 (2020-07-08)

### New features

Resolved positions have a new convenience method, `posAtIndex`, which can resolve a depth and index to a position.

## 1.10.1 (2020-07-08)

### Bug fixes

Fix a bug that prevented non-canonical list structure from being normalized.

## 1.10.0 (2020-05-25)

### Bug fixes

Avoid fixing directly nested list nodes during DOM parsing when it looks like the schema allows those.

### New features

DOM parser rules can now specify `closeParent: true` to have the effect of closing their parent node when matched.

## 1.9.1 (2020-01-17)

### Bug fixes

Marks found in the DOM at the wrong level (for example, a bold style on a block node) are now properly moved to the node content.

## 1.9.0 (2020-01-07)

### New features

The `NodeType` method [`hasRequiredAttrs`](https://prosemirror.net/docs/ref/#model.NodeType.hasRequiredAttrs) is now public.

Element and attribute names in [`DOMOutputSpec`](https://prosemirror.net/docs/ref/#model.DOMOutputSpec) structures can now contain namespaces.

## 1.8.2 (2019-11-20)

### Bug fixes

Rename ES module files to use a .js extension, since Webpack gets confused by .mjs

## 1.8.1 (2019-11-19)

### Bug fixes

The file referred to in the package's `module` field now is compiled down to ES5.

## 1.8.0 (2019-11-08)

### New features

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

Add a `module` field to package json file.

## 1.7.5 (2019-11-07)

### Bug fixes

`ContentMatch.edge` now throws, as it is supposed to, when you try to access the edge past the last one.

## 1.7.4 (2019-10-10)

### Bug fixes

Fix an issue where `fillBefore` would in some cases insert unneccesary optional child nodes in the generated content.

## 1.7.3 (2019-10-03)

### Bug fixes

Fix an issue where _any_ whitespace (not just the characters that HTML collapses) was collapsed by the parser in non-whitespace-preserving mode.

## 1.7.2 (2019-09-04)

### Bug fixes

When `
` DOM nodes can't be parsed normally, the parser now converts them to newlines. This should improve parsing of some forms of source code HTML. ## 1.7.1 (2019-05-31) ### Bug fixes Using `Fragment.from` on an invalid value, including a `Fragment` instance from a different version/instance of the library, now raises a meaningful error rather than getting confused. Fix a bug in parsing overlapping marks of the same non-self-exclusive type. ## 1.7.0 (2019-01-29) ### New features Mark specs now support a property [`spanning`](https://prosemirror.net/docs/ref/#model.MarkSpec.spanning) which, when set to `false`, prevents the mark's DOM markup from spanning multiple nodes, so that a separate wrapper is created for each adjacent marked node. ## 1.6.4 (2019-01-05) ### Bug fixes Don't output empty style attributes when a style property with a null value is present in `renderSpec`. ## 1.6.3 (2018-10-26) ### Bug fixes The DOM parser now drops whitespace after BR nodes when not in whitespace-preserving mode. ## 1.6.2 (2018-10-01) ### Bug fixes Prevent [`ContentMatch.findWrapping`](https://prosemirror.net/docs/ref/#model.ContentMatch.findWrapping) from returning node types with required attributes. ## 1.6.1 (2018-07-24) ### Bug fixes Fix a bug where marks were sometimes parsed incorrectly. ## 1.6.0 (2018-07-20) ### Bug fixes Fix issue where marks would be applied to the wrong node when parsing a slice from DOM. ### New features Adds a new node spec property, [`toDebugString`](https://prosemirror.net/docs/ref/#model.NodeSpec.toDebugString), making it possible to customize your nodes' `toString` behavior. ## 1.5.0 (2018-05-31) ### New features [`ParseRule.getContent`](https://prosemirror.net/docs/ref/#model.ParseRule.getContent) is now passed the parser schema as second argument. ## 1.4.4 (2018-05-03) ### Bug fixes Fix a regression where `DOMParser.parse` would fail to apply mark nodes directly at the start of the input. ## 1.4.3 (2018-04-27) ### Bug fixes [`DOMParser.parseSlice`](https://prosemirror.net/docs/ref/#model.DOMParser.parseSlice) can now correctly parses marks at the top level again. ## 1.4.2 (2018-04-15) ### Bug fixes Remove a `console.log` that was accidentally left in the previous release. ## 1.4.1 (2018-04-13) ### Bug fixes `DOMParser` can now parse marks on block nodes. ## 1.4.0 (2018-03-22) ### New features [`ContentMatch.defaultType`](https://prosemirror.net/docs/ref/#model.ContentMatch.defaultType), a way to get a matching node type at a content match position, is now public. ## 1.3.0 (2018-03-22) ### New features `ContentMatch` objects now have an [`edgeCount`](https://prosemirror.net/docs/ref/#model.ContentMatch.edgeCount) property and an [`edge`](https://prosemirror.net/docs/ref/#model.ContentMatch.edge) method, providing direct access to the finite automaton structure. ## 1.2.2 (2018-03-15) ### Bug fixes Throw errors, rather than constructing invalid objects, when deserializing from invalid JSON data. ## 1.2.1 (2018-03-15) ### Bug fixes Content expressions with text nodes in required positions now raise the appropriate error about being unable to generate such nodes. ## 1.2.0 (2018-03-14) ### Bug fixes [`rangeHasMark`](https://prosemirror.net/docs/ref/#model.Node.rangeHasMark) now always returns false for empty ranges. The DOM renderer no longer needlessly splits mark nodes when starting a non-rendered mark. ### New features [`DOMSerializer`](https://prosemirror.net/docs/ref/#model.DOMSerializer) now allows [DOM specs](https://prosemirror.net/docs/ref/#model.DOMOutputSpec) for marks to have holes in them, to specify the precise position where their content should be rendered. The base position parameter to [`Node.nodesBetween`](https://prosemirror.net/docs/ref/#model.Node.nodesBetween) and [`Fragment.nodesBetween`](https://prosemirror.net/docs/ref/#model.Fragment.nodesBetween) is now part of the public interface. ## 1.1.0 (2018-01-05) ### New features [`Slice.maxOpen`](https://prosemirror.net/docs/ref/#model.Slice^maxOpen) now has a second argument that can be used to prevent it from opening isolating nodes. ## 1.0.1 (2017-11-10) ### Bug fixes [`ReplaceError`](https://prosemirror.net/docs/ref/#model.ReplaceError) instances now properly inherit from `Error`. ## 1.0.0 (2017-10-13) ### New features [`ParseRule.context`](https://prosemirror.net/docs/ref/#model.ParseRule.context) may now include multiple, pipe-separated context expressions. ## 0.23.1 (2017-09-21) ### Bug fixes `NodeType.allowsMarks` and `allowedMarks` now actually work for nodes that allow only specific marks. ## 0.23.0 (2017-09-13) ### Breaking changes [`ResolvedPos.marks`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ResolvedPos.marks) no longer takes a parameter (you probably want [`marksAcross`](https://prosemirror.net/doc/ref/version/0.23.0.html#model.ResolvedPos.marksAcross) if you were passing true there). Attribute and mark constraints in [content expressions](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeSpec.content) are no longer supported (this also means the `prosemirror-schema-table` package, which relied on them, is no longer supported). In this release, mark constraints are still (approximately) recognized with a warning, when present. [`ContentMatch`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ContentMatch) objects lost a number of methods: `matchNode`, `matchToEnd`, `findWrappingFor` (which can be easily emulated using the remaining API), and `allowsMark`, which is now the responsibility of [node types](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeType.allowsMarkType) instead. [`ContentMatch.validEnd`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ContentMatch.validEnd) is now a property rather than a method. [`ContentMatch.findWrapping`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ContentMatch.findWrapping) now returns an array of plain node types, with no attribute information (since this is no longer necessary). The `compute` method for attributes is no longer supported. Fragments no longer have an `offsetAt` method. `DOMParser.schemaRules` is no longer public (use [`fromSchema`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.DOMParser^fromSchema) and get the resulting parser's `rules` property instead). The DOM parser option `topStart` has been replaced by [`topMatch`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ParseOptions.topMatch). The `DOMSerializer` methods `nodesFromSchema` and `marksFromSchema` are no longer public (construct a serializer with [`fromSchema`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.DOMSerializer^fromSchema) and read its `nodes` and `marks` properties instead). ### Bug fixes Fix issue where whitespace at node boundaries was sometimes dropped during content parsing. Attribute default values of `undefined` are now allowed. ### New features [`contentElement`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ParseRule.contentElement) in parse rules may now be a function. The new method [`ResolvedPos.marksAcross`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ResolvedPos.marksAcross) can be used to find the set of marks that should be preserved after a deletion. [Content expressions](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeSpec.content) are now a regular language, meaning all the operators can be nested and composed as desired, and a bunch of constraints on what could appear next to what have been lifted. The starting content match for a node type now lives in [`NodeType.contentMatch`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeType.contentMatch). Allowed marks are now specified per node, rather than in content expressions, using the [`marks`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeSpec.marks) property on the node spec. Node types received new methods [`allowsMarkType`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeType.allowsMarkType), [`allowsMarks`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeType.allowsMarks), and [`allowedMarks`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.NodeType.allowedMarks), which tell you about the marks that node supports. The [`style`](https://prosemirror.net/docs/ref/version/0.23.0.html#model.ParseRule.style) property on parse rules may now have the form `"font-style=italic"` to only match styles that have the value after the equals sign. ## 0.22.0 (2017-06-29) ### Bug fixes When using [`parseSlice`](https://prosemirror.net/docs/ref/version/0.22.0.html#model.DOMParser.parseSlice), inline DOM content wrapped in block elements for which no parse rule is defined will now be properly wrapped in a textblock node. ### New features [Resolved positions](https://prosemirror.net/docs/ref/version/0.22.0.html#model.ResolvedPos) now have a [`doc`](https://prosemirror.net/docs/ref/version/0.22.0.html#model.ResolvedPos.doc) accessor to easily get their root node. Parse rules now support a [`namespace` property](https://prosemirror.net/docs/ref/version/0.22.0.html#model.ParseRule.namespace) to match XML namespaces. The [`NodeRange`](https://prosemirror.net/docs/ref/version/0.22.0.html#model.NodeRange) constructor is now public (whereas before you could only construct these through [`blockRange`](https://prosemirror.net/docs/ref/version/0.22.0.html#model.ResolvedPos.blockRange)). ## 0.21.0 (2017-05-03) ### Breaking changes The `openLeft` and `openRight` properties of `Slice` objects have been renamed to [`openStart`](https://prosemirror.net/docs/ref/version/0.21.0.html#model.Slice.openStart) and [`openEnd`](https://prosemirror.net/docs/ref/version/0.21.0.html#model.Slice.openEnd) to avoid confusion in right-to-left text. The old names will continue to work with a warning until the next release. ### New features Mark [serializing functions](https://prosemirror.net/docs/ref/version/0.21.0.html#model.MarkSpec.toDOM) now get a second parameter that indicates whether the mark's content is inline or block nodes. Setting a mark serializer to `null` in a [`DOMSerializer`](https://prosemirror.net/docs/ref/version/0.21.0.html#model.DOMSerializer) can now be used to omit that mark when serializing. Node specs support a new property [`isolating`](https://prosemirror.net/docs/ref/version/0.21.0.html#model.NodeSpec.isolating), which is used to disable editing actions like backspacing and lifting across such a node's boundaries. ## 0.20.0 (2017-04-03) ### Breaking changes Newlines in the text are now normalized to spaces when parsing except when you set `preserveWhitespace` to `"full"` in your [options](https://prosemirror.net/docs/ref/version/0.20.0.html#model.DOMParser.parse) or in a [parse rule](https://prosemirror.net/docs/ref/version/0.20.0.html#model.ParseRule.preserveWhitespace). ### Bug fixes Fix crash in IE when parsing DOM content. ### New features Fragments now have [`nodesBetween`](https://prosemirror.net/docs/ref/version/0.20.0.html#model.Fragment.nodesBetween) and [`descendants`](https://prosemirror.net/docs/ref/version/0.20.0.html#model.Fragments.descendants) methods, providing the same functionality as the methods by the same name on nodes. Resolved positions now have [`max`](https://prosemirror.net/docs/ref/version/0.20.0.html#model.ResolvedPos.max) and [`min`](https://prosemirror.net/docs/ref/version/0.20.0.html#model.ResolvedPos.min) methods to easily find a maximum or minimum position. ## 0.19.0 (2017-03-16) ### Breaking changes `MarkSpec.inclusiveRight` was replaced by [`inclusive`](https://prosemirror.net/docs/ref/version/0.19.0.html#model.MarkSpec.inclusive), which behaves slightly differently. `inclusiveRight` will be interpreted as `inclusive` (with a warning) until the next release. ### New features The new [`inlineContent`](https://prosemirror.net/docs/ref/version/0.19.0.html#model.Node.inlineContent) property on nodes and node types tells you whether a node type supports inline content. [`MarkSpec.inclusive`](https://prosemirror.net/docs/ref/version/0.19.0.html#model.MarkSpec.inclusive) can now be used to control whether content inserted at the boundary of a mark receives that mark. Parse rule [`context`](https://prosemirror.net/docs/ref/version/0.19.0.html#model.ParseRule.context) restrictions can now use node [groups](https://prosemirror.net/docs/ref/version/0.19.0.html#model.NodeSpec.group), not just node names, to specify valid context. ## 0.18.0 (2017-02-24) ### Breaking changes `schema.nodeSpec` and `schema.markSpec` have been deprecated in favor of [`schema.spec`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.Schema.spec). The properties still work with a warning in this release, but will be dropped in the next. ### New features `Node` objects now have a [`check`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.Node.check) method which can be used to assert that they conform to the schema. Node specs now support an [`atom` property](https://prosemirror.net/docs/ref/version/0.18.0.html#model.NodeSpec.atom), and nodes an [`isAtom` accessor](https://prosemirror.net/docs/ref/version/0.18.0.html#model.Node.isAtom), which is currently only used to determine whether such nodes should be directly selectable (for example when they are rendered as an uneditable node view). The new [`excludes`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.MarkSpec.excludes) field on mark specs can be used to control the marks that this mark may coexist with. Mark type objects also gained an [`excludes` _method_](https://prosemirror.net/docs/ref/version/0.18.0.html#model.MarkType.excludes) to querty this relation. Mark specs now support a [`group`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.MarkSpec.group) property, and marks can be referred to by group name in content specs. The `Schema` class now provides its whole [spec](https://prosemirror.net/docs/ref/version/0.18.0.html#model.SchemaSpec) under its [`spec`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.Schema.spec) property. The name of a schema's default top-level node is now [configurable](https://prosemirror.net/docs/ref/version/0.18.0.html#model.SchemaSpec.topNode). You can use [`schema.topNodeType`](https://prosemirror.net/docs/ref/version/0.18.0.html#model.Schema.topNodeType) to retrieve the top node type. [Parse rules](https://prosemirror.net/docs/ref/version/0.18.0.html#model.ParseRule) now support a [`context` field](https://prosemirror.net/docs/ref/version/0.18.0.html#model.ParseRule.context) that can be used to only make the rule match inside certain ancestor nodes. ## 0.17.0 (2017-01-05) ### Breaking changes `Node.marksAt` was replaced with [`ResolvedPos.marks`](https://prosemirror.net/docs/ref/version/0.17.0.html#model.ResolvedPos.marks). It still works (with a warning) in this release, but will be removed in the next one. ## 0.15.0 (2016-12-10) ### Breaking changes `ResolvedPos.atNodeBoundary` is deprecated and will be removed in the next release. Use `textOffset > 0` instead. ### New features Parse rules associated with a schema can now specify a [`priority`](https://prosemirror.net/docs/ref/version/0.15.0.html#model.ParseRule.priority) to influence the order in which they are applied. Resolved positions have a new getter [`textOffset`](https://prosemirror.net/docs/ref/version/0.15.0.html#model.ResolvedPos.textOffset) to find their position within a text node (if any). ## 0.14.1 (2016-11-30) ### Bug fixes [`DOMParser.parseSlice`](https://prosemirror.net/docs/ref/version/0.14.0.html#model.DOMParser.parseSlice) will now ignore whitespace-only text nodes at the top of the slice. ## 0.14.0 (2016-11-28) ### New features Parse rules now support [`skip`](https://prosemirror.net/docs/ref/version/0.14.0.html#model.ParseRule.skip) (skip outer element, parse content) and [`getContent`](https://prosemirror.net/docs/ref/version/0.14.0.html#model.ParseRule.getContent) (compute content using custom code) properties. The `DOMSerializer` class now exports a static [`renderSpec`](https://prosemirror.net/docs/ref/version/0.14.0.html#model.DOMSerializer^renderSpec) method that can help render DOM spec arrays. ## 0.13.0 (2016-11-11) ### Breaking changes `ResolvedPos.sameDepth` is now called [`ResolvedPos.sharedDepth`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.ResolvedPos.sharedDepth), and takes a raw, unresolved position as argument. ### New features [`DOMSerializer`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.DOMSerializer)'s `nodes` and `marks` properties are now public. [`ContentMatch.findWrapping`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.ContentMatch.findWrapping) now takes a third argument, `marks`. There's a new method [`findWrappingFor`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.ContentMatch.findWrappingFor) that accepts a whole node. Adds [`Slice.maxOpen`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.Slice^maxOpen) static method to create maximally open slices. DOM parser objects now have a [`parseSlice`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.DOMParser.parseSlice) method which parses an HTML fragment into a [`Slice`](https://prosemirror.net/docs/ref/version/0.13.0.html#model.Slice), rather than trying to create a whole document from it. ## 0.12.0 (2016-10-21) ### Breaking changes Drops support for some undocumented options to the DOM serializer that were used by the view. ### Bug fixes When rendering DOM attributes, only ignore null values, not all falsy values. ## 0.11.0 (2016-09-21) ### Breaking changes Moved into a separate module. The JSON representation of [marks](https://prosemirror.net/docs/ref/version/0.11.0.html#model.Mark) has changed from `{"_": "type", "attr1": "value"}` to `{"type": "type", "attrs": {"attr1": "value"}}`, where `attrs` may be omitted when the mark has no attributes. Mark-related JSON methods now live on the [`Mark` class](https://prosemirror.net/docs/ref/version/0.11.0.html#model.Mark^fromJSON). The way node and mark types in a schema are defined was changed from defining subclasses to passing plain objects ([`NodeSpec`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.NodeSpec) and [`MarkSpec`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.MarkSpec)). DOM serialization and parsing logic is now done through dedicated objects ([`DOMSerializer`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.DOMSerializer) and [`DOMParser`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.DOMParser)), rather than through the schema. It is now possible to define alternative parsing and serializing strategies without touching the schema. ### New features The [`Slice`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.Slice) class now has an [`eq` method](https://prosemirror.net/docs/ref/version/0.11.0.html#model.Slice.eq). The [`Node.marksAt`](https://prosemirror.net/docs/ref/version/0.11.0.html#model.Node.marksAt) method got a second parameter to indicate you're interested in the marks _after_ the position. prosemirror-model/CONTRIBUTING.md000066400000000000000000000073301525422544500170030ustar00rootroot00000000000000# How to contribute - [Getting help](#getting-help) - [Submitting bug reports](#submitting-bug-reports) - [Contributing code](#contributing-code) ## Getting help Community discussion, questions, and informal bug reporting is done on the [discuss.ProseMirror forum](http://discuss.prosemirror.net). ## Submitting bug reports Report bugs on the [issue tracker](https://code.haverbeke.berlin/prosemirror/prosemirror/issues). Before reporting a bug, please read these pointers. - The issue tracker is for *bugs*, not requests for help. Questions should be asked on the [forum](http://discuss.prosemirror.net). - Include information about the version of the code that exhibits the problem. For browser-related issues, include the browser and browser version on which the problem occurred. - Mention very precisely what went wrong. "X is broken" is not a good bug report. What did you expect to happen? What happened instead? Describe the exact steps a maintainer has to take to make the problem occur. A screencast can be useful, but is no substitute for a textual description. - A great way to make it easy to reproduce your problem, if it can not be trivially reproduced on the website demos, is to submit a script that triggers the issue. ## Contributing code Code generated by a language model is not welcome in this project. Please don't waste my time with it. If you want to make a change that involves a significant overhaul of the code or introduces a user-visible new feature, create an [issue](https://code.haverbeke.berlin/prosemirror/prosemirror/issues/) first with your proposal. - Make sure you have a [Codeberg](https://codeberg.org/user/sign_up) or [GitHub](https://github.com/signup/free) account. - Use that to create a [code.haverbeke.berlin account](https://code.haverbeke.berlin/user/login). - Fork the relevant repository. - Create a local checkout of the code. You can use the [main repository](https://code.haverbeke.berlin/prosemirror/prosemirror) to easily check out all core modules. - Make your changes, and commit them - If your changes are easy to test or likely to regress, add tests in the relevant `test/` directory. Either put them in an existing `test-*.js` file, if they fit there, or add a new file. - Make sure all tests pass. Run `npm run test` to verify tests pass (you will need Node.js v6+). - Submit a pull request. Don't put more than one feature/fix in a single pull request. By contributing code to ProseMirror you - Agree to license the contributed code under the project's [MIT license](https://code.haverbeke.berlin/prosemirror/prosemirror/blob/main/LICENSE). - Confirm that you have the right to contribute and license the code in question. (Either you hold all rights on the code, or the rights holder has explicitly granted the right to use it like this, through a compatible open source license or through a direct agreement with you.) ### Coding standards - ES6 syntax, targeting an ES5 runtime (i.e. don't use library elements added by ES6, don't use ES7/ES.next syntax). - 2 spaces per indentation level, no tabs. - No semicolons except when necessary. - Follow the surrounding code when it comes to spacing, brace placement, etc. - Brace-less single-statement bodies are encouraged whenever they don't impact readability. - [getdocs-ts](https://code.haverbeke.berlin/marijn/getdocs-ts)-style doc comments above items that are part of the public API. - When documenting non-public items, you can put the type after a single colon, so that getdocs doesn't pick it up and add it to the API reference. - ProseMirror does *not* follow JSHint or JSLint prescribed style. Patches that try to 'fix' code to pass one of these linters will not be accepted. prosemirror-model/LICENSE000066400000000000000000000021211525422544500155500ustar00rootroot00000000000000Copyright (C) 2015-2017 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. prosemirror-model/README.md000066400000000000000000000027501525422544500160320ustar00rootroot00000000000000# prosemirror-model [ [**WEBSITE**](https://prosemirror.net) | [**ISSUES**](https://code.haverbeke.berlin/prosemirror/prosemirror/issues) | [**FORUM**](https://discuss.prosemirror.net) | [**CHANGELOG**](https://code.haverbeke.berlin/prosemirror/prosemirror-model/src/branch/main/CHANGELOG.md) ] This is a [core module](https://prosemirror.net/docs/ref/#model) of [ProseMirror](https://prosemirror.net). ProseMirror is a well-behaved rich semantic content editor based on contentEditable, with support for collaborative editing and custom document schemas. This [module](https://prosemirror.net/docs/ref/#model) implements ProseMirror's [document model](https://prosemirror.net/docs/guide/#doc), along with the mechanisms needed to support [schemas](https://prosemirror.net/docs/guide/#schema). The [project page](https://prosemirror.net) has more information, a number of [examples](https://prosemirror.net/examples/) and the [documentation](https://prosemirror.net/docs/). This code is released under an [MIT license](https://code.haverbeke.berlin/prosemirror/prosemirror/src/branch/main/LICENSE). There's a [forum](http://discuss.prosemirror.net) for general discussion and support requests, and the [bug tracker](https://code.haverbeke.berlin/prosemirror/prosemirror/issues) is the place to report issues. We aim to be an inclusive, welcoming community. To make that explicit, we have a [code of conduct](http://contributor-covenant.org/version/1/1/0/) that applies to communication around the project. prosemirror-model/package.json000066400000000000000000000016041525422544500170360ustar00rootroot00000000000000{ "name": "prosemirror-model", "version": "1.25.12", "description": "ProseMirror's document model", "type": "module", "main": "dist/index.cjs", "module": "dist/index.js", "types": "dist/index.d.ts", "exports": { "import": "./dist/index.js", "require": "./dist/index.cjs" }, "sideEffects": false, "license": "MIT", "maintainers": [ { "name": "Marijn Haverbeke", "email": "marijn@haverbeke.berlin", "web": "http://marijnhaverbeke.nl" } ], "repository": { "type": "git", "url": "git+https://code.haverbeke.berlin/prosemirror/prosemirror-model.git" }, "dependencies": { "orderedmap": "^2.0.0" }, "devDependencies": { "@prosemirror/buildhelper": "^0.1.5", "jsdom": "^20.0.0", "prosemirror-test-builder": "^1.0.0" }, "scripts": { "test": "pm-runtests", "prepare": "pm-buildhelper src/index.ts" } } prosemirror-model/src/000077500000000000000000000000001525422544500153365ustar00rootroot00000000000000prosemirror-model/src/README.md000066400000000000000000000025161525422544500166210ustar00rootroot00000000000000This module defines ProseMirror's content model, the data structures used to represent and work with documents. ### Document Structure A ProseMirror document is a tree. At each level, a [node](#model.Node) describes the type of the content, and holds a [fragment](#model.Fragment) containing its children. @Node @Fragment @Mark @Slice @Attrs @ReplaceError ### Resolved Positions Positions in a document can be represented as integer [offsets](/docs/guide/#doc.indexing). But you'll often want to use a more convenient representation. @ResolvedPos @NodeRange ### Document Schema Every ProseMirror document conforms to a [schema](/docs/guide/#schema), which describes the set of nodes and marks that it is made out of, along with the relations between those, such as which node may occur as a child node of which other nodes. @Schema @SchemaSpec @NodeSpec @MarkSpec @AttributeSpec @NodeType @MarkType @ContentMatch ### DOM Representation Because representing a document as a tree of DOM nodes is central to the way ProseMirror operates, DOM [parsing](#model.DOMParser) and [serializing](#model.DOMSerializer) is integrated with the model. (But note that you do _not_ need to have a DOM implementation loaded to use this module.) @DOMParser @ParseOptions @GenericParseRule @TagParseRule @StyleParseRule @ParseRule @DOMSerializer @DOMOutputSpec prosemirror-model/src/comparedeep.ts000066400000000000000000000010261525422544500201710ustar00rootroot00000000000000export function compareDeep(a: any, b: any) { if (a === b) return true if (!(a && typeof a == "object") || !(b && typeof b == "object")) return false let array = Array.isArray(a) if (Array.isArray(b) != array) return false if (array) { if (a.length != b.length) return false for (let i = 0; i < a.length; i++) if (!compareDeep(a[i], b[i])) return false } else { for (let p in a) if (!(p in b) || !compareDeep(a[p], b[p])) return false for (let p in b) if (!(p in a)) return false } return true } prosemirror-model/src/content.ts000066400000000000000000000334331525422544500173660ustar00rootroot00000000000000import {Fragment} from "./fragment" import {NodeType} from "./schema" type MatchEdge = {type: NodeType, next: ContentMatch} /// Instances of this class represent a match state of a node type's /// [content expression](#model.NodeSpec.content), and can be used to /// find out whether further content matches here, and whether a given /// position is a valid end of the node. export class ContentMatch { /// @internal readonly next: MatchEdge[] = [] /// @internal readonly wrapCache: (NodeType | readonly NodeType[] | null)[] = [] /// @internal constructor( /// True when this match state represents a valid end of the node. readonly validEnd: boolean ) {} /// @internal static parse(string: string, nodeTypes: {readonly [name: string]: NodeType}): ContentMatch { let stream = new TokenStream(string, nodeTypes) if (stream.next == null) return ContentMatch.empty let expr = parseExpr(stream) if (stream.next) stream.err("Unexpected trailing text") let match = dfa(nfa(expr)) checkForDeadEnds(match, stream) return match } /// Match a node type, returning a match after that node if /// successful. matchType(type: NodeType): ContentMatch | null { for (let i = 0; i < this.next.length; i++) if (this.next[i].type == type) return this.next[i].next return null } /// Try to match a fragment. Returns the resulting match when /// successful. matchFragment(frag: Fragment, start = 0, end = frag.childCount): ContentMatch | null { let cur: ContentMatch | null = this for (let i = start; cur && i < end; i++) cur = cur.matchType(frag.child(i).type) return cur } /// @internal get inlineContent() { return this.next.length != 0 && this.next[0].type.isInline } /// Get the first matching node type at this match position that can /// be generated. get defaultType(): NodeType | null { for (let i = 0; i < this.next.length; i++) { let {type} = this.next[i] if (!(type.isText || type.hasRequiredAttrs())) return type } return null } /// @internal compatible(other: ContentMatch) { for (let i = 0; i < this.next.length; i++) for (let j = 0; j < other.next.length; j++) if (this.next[i].type == other.next[j].type) return true return false } /// Try to match the given fragment, and if that fails, see if it can /// be made to match by inserting nodes in front of it. When /// successful, return a fragment of inserted nodes (which may be /// empty if nothing had to be inserted). When `toEnd` is true, only /// return a fragment if the resulting match goes to the end of the /// content expression. fillBefore(after: Fragment, toEnd = false, startIndex = 0): Fragment | null { let seen: ContentMatch[] = [this] function search(match: ContentMatch, types: readonly NodeType[]): Fragment | null { let finished = match.matchFragment(after, startIndex) if (finished && (!toEnd || finished.validEnd)) return Fragment.from(types.map(tp => tp.createAndFill()!)) for (let i = 0; i < match.next.length; i++) { let {type, next} = match.next[i] if (!(type.isText || type.hasRequiredAttrs()) && seen.indexOf(next) == -1) { seen.push(next) let found = search(next, types.concat(type)) if (found) return found } } return null } return search(this, []) } /// Find a set of wrapping node types that would allow a node of the /// given type to appear at this position. The result may be empty /// (when it fits directly) and will be null when no such wrapping /// exists. findWrapping(target: NodeType): readonly NodeType[] | null { for (let i = 0; i < this.wrapCache.length; i += 2) if (this.wrapCache[i] == target) return this.wrapCache[i + 1] as (readonly NodeType[] | null) let computed = this.computeWrapping(target) this.wrapCache.push(target, computed) return computed } /// @internal computeWrapping(target: NodeType): readonly NodeType[] | null { type Active = {match: ContentMatch, type: NodeType | null, via: Active | null} let seen = Object.create(null), active: Active[] = [{match: this, type: null, via: null}] while (active.length) { let current = active.shift()!, match = current.match if (match.matchType(target)) { let result: NodeType[] = [] for (let obj: Active = current; obj.type; obj = obj.via!) result.push(obj.type) return result.reverse() } for (let i = 0; i < match.next.length; i++) { let {type, next} = match.next[i] if (!type.isLeaf && !type.hasRequiredAttrs() && !(type.name in seen) && (!current.type || next.validEnd)) { active.push({match: type.contentMatch, type, via: current}) seen[type.name] = true } } } return null } /// The number of outgoing edges this node has in the finite /// automaton that describes the content expression. get edgeCount() { return this.next.length } /// Get the _n_โ€‹th outgoing edge from this node in the finite /// automaton that describes the content expression. edge(n: number): MatchEdge { if (n >= this.next.length) throw new RangeError(`There's no ${n}th edge in this content match`) return this.next[n] } /// @internal toString() { let seen: ContentMatch[] = [] function scan(m: ContentMatch) { seen.push(m) for (let i = 0; i < m.next.length; i++) if (seen.indexOf(m.next[i].next) == -1) scan(m.next[i].next) } scan(this) return seen.map((m, i) => { let out = i + (m.validEnd ? "*" : " ") + " " for (let i = 0; i < m.next.length; i++) out += (i ? ", " : "") + m.next[i].type.name + "->" + seen.indexOf(m.next[i].next) return out }).join("\n") } /// @internal static empty = new ContentMatch(true) } class TokenStream { inline: boolean | null = null pos = 0 tokens: string[] constructor( readonly string: string, readonly nodeTypes: {readonly [name: string]: NodeType} ) { this.tokens = string.split(/\s*(?=\b|\W|$)/) if (this.tokens[this.tokens.length - 1] == "") this.tokens.pop() if (this.tokens[0] == "") this.tokens.shift() } get next() { return this.tokens[this.pos] } eat(tok: string) { return this.next == tok && (this.pos++ || true) } err(str: string): never { throw new SyntaxError(str + " (in content expression '" + this.string + "')") } } type Expr = {type: "choice", exprs: Expr[]} | {type: "seq", exprs: Expr[]} | {type: "plus", expr: Expr} | {type: "star", expr: Expr} | {type: "opt", expr: Expr} | {type: "range", min: number, max: number, expr: Expr} | {type: "name", value: NodeType} function parseExpr(stream: TokenStream): Expr { let exprs: Expr[] = [] do { exprs.push(parseExprSeq(stream)) } while (stream.eat("|")) return exprs.length == 1 ? exprs[0] : {type: "choice", exprs} } function parseExprSeq(stream: TokenStream): Expr { let exprs: Expr[] = [] do { exprs.push(parseExprSubscript(stream)) } while (stream.next && stream.next != ")" && stream.next != "|") return exprs.length == 1 ? exprs[0] : {type: "seq", exprs} } function parseExprSubscript(stream: TokenStream): Expr { let expr = parseExprAtom(stream) for (;;) { if (stream.eat("+")) expr = {type: "plus", expr} else if (stream.eat("*")) expr = {type: "star", expr} else if (stream.eat("?")) expr = {type: "opt", expr} else if (stream.eat("{")) expr = parseExprRange(stream, expr) else break } return expr } function parseNum(stream: TokenStream) { if (/\D/.test(stream.next)) stream.err("Expected number, got '" + stream.next + "'") let result = Number(stream.next) stream.pos++ return result } function parseExprRange(stream: TokenStream, expr: Expr): Expr { let min = parseNum(stream), max = min if (stream.eat(",")) { if (stream.next != "}") max = parseNum(stream) else max = -1 } if (!stream.eat("}")) stream.err("Unclosed braced range") return {type: "range", min, max, expr} } function resolveName(stream: TokenStream, name: string): readonly NodeType[] { let types = stream.nodeTypes, type = types[name] if (type) return [type] let result: NodeType[] = [] for (let typeName in types) { let type = types[typeName] if (type.isInGroup(name)) result.push(type) } if (result.length == 0) stream.err("No node type or group '" + name + "' found") return result } function parseExprAtom(stream: TokenStream): Expr { if (stream.eat("(")) { let expr = parseExpr(stream) if (!stream.eat(")")) stream.err("Missing closing paren") return expr } else if (!/\W/.test(stream.next)) { let exprs = resolveName(stream, stream.next).map(type => { if (stream.inline == null) stream.inline = type.isInline else if (stream.inline != type.isInline) stream.err("Mixing inline and block content") return {type: "name", value: type} as Expr }) stream.pos++ return exprs.length == 1 ? exprs[0] : {type: "choice", exprs} } else { stream.err("Unexpected token '" + stream.next + "'") } } // The code below helps compile a regular-expression-like language // into a deterministic finite automaton. For a good introduction to // these concepts, see https://swtch.com/~rsc/regexp/regexp1.html type Edge = {term: NodeType | undefined, to: number | undefined} // Construct an NFA from an expression as returned by the parser. The // NFA is represented as an array of states, which are themselves // arrays of edges, which are `{term, to}` objects. The first state is // the entry state and the last node is the success state. // // Note that unlike typical NFAs, the edge ordering in this one is // significant, in that it is used to contruct filler content when // necessary. function nfa(expr: Expr): Edge[][] { let nfa: Edge[][] = [[]] connect(compile(expr, 0), node()) return nfa function node() { return nfa.push([]) - 1 } function edge(from: number, to?: number, term?: NodeType) { let edge = {term, to} nfa[from].push(edge) return edge } function connect(edges: Edge[], to: number) { edges.forEach(edge => edge.to = to) } function compile(expr: Expr, from: number): Edge[] { if (expr.type == "choice") { return expr.exprs.reduce((out, expr) => out.concat(compile(expr, from)), [] as Edge[]) } else if (expr.type == "seq") { for (let i = 0;; i++) { let next = compile(expr.exprs[i], from) if (i == expr.exprs.length - 1) return next connect(next, from = node()) } } else if (expr.type == "star") { let loop = node() edge(from, loop) connect(compile(expr.expr, loop), loop) return [edge(loop)] } else if (expr.type == "plus") { let loop = node() connect(compile(expr.expr, from), loop) connect(compile(expr.expr, loop), loop) return [edge(loop)] } else if (expr.type == "opt") { return [edge(from)].concat(compile(expr.expr, from)) } else if (expr.type == "range") { let cur = from for (let i = 0; i < expr.min; i++) { let next = node() connect(compile(expr.expr, cur), next) cur = next } if (expr.max == -1) { connect(compile(expr.expr, cur), cur) } else { for (let i = expr.min; i < expr.max; i++) { let next = node() edge(cur, next) connect(compile(expr.expr, cur), next) cur = next } } return [edge(cur)] } else if (expr.type == "name") { return [edge(from, undefined, expr.value)] } else { throw new Error("Unknown expr type") } } } function cmp(a: number, b: number) { return b - a } // Get the set of nodes reachable by null edges from `node`. Omit // nodes with only a single null-out-edge, since they may lead to // needless duplicated nodes. function nullFrom(nfa: Edge[][], node: number): readonly number[] { let result: number[] = [] scan(node) return result.sort(cmp) function scan(node: number): void { let edges = nfa[node] if (edges.length == 1 && !edges[0].term) return scan(edges[0].to!) result.push(node) for (let i = 0; i < edges.length; i++) { let {term, to} = edges[i] if (!term && result.indexOf(to!) == -1) scan(to!) } } } // Compiles an NFA as produced by `nfa` into a DFA, modeled as a set // of state objects (`ContentMatch` instances) with transitions // between them. function dfa(nfa: Edge[][]): ContentMatch { let labeled = Object.create(null) return explore(nullFrom(nfa, 0)) function explore(states: readonly number[]) { let out: [NodeType, number[]][] = [] states.forEach(node => { nfa[node].forEach(({term, to}) => { if (!term) return let set: number[] | undefined for (let i = 0; i < out.length; i++) if (out[i][0] == term) set = out[i][1] nullFrom(nfa, to!).forEach(node => { if (!set) out.push([term, set = []]) if (set.indexOf(node) == -1) set.push(node) }) }) }) let state = labeled[states.join(",")] = new ContentMatch(states.indexOf(nfa.length - 1) > -1) for (let i = 0; i < out.length; i++) { let states = out[i][1].sort(cmp) state.next.push({type: out[i][0], next: labeled[states.join(",")] || explore(states)}) } return state } } function checkForDeadEnds(match: ContentMatch, stream: TokenStream) { for (let i = 0, work = [match]; i < work.length; i++) { let state = work[i], dead = !state.validEnd, nodes: string[] = [] for (let j = 0; j < state.next.length; j++) { let {type, next} = state.next[j] nodes.push(type.name) if (dead && !(type.isText || type.hasRequiredAttrs())) dead = false if (work.indexOf(next) == -1) work.push(next) } if (dead) stream.err("Only non-generatable nodes (" + nodes.join(", ") + ") in a required position (see https://prosemirror.net/docs/guide/#generatable)") } } prosemirror-model/src/diff.ts000066400000000000000000000041521525422544500166200ustar00rootroot00000000000000import {Fragment} from "./fragment" export function findDiffStart(a: Fragment, b: Fragment, pos: number): number | null { for (let i = 0;; i++) { if (i == a.childCount || i == b.childCount) return a.childCount == b.childCount ? null : pos let childA = a.child(i), childB = b.child(i) if (childA == childB) { pos += childA.nodeSize; continue } if (!childA.sameMarkup(childB)) return pos if (childA.isText && childA.text != childB.text) { let tA = childA.text!, tB = childB.text!, j = 0 for (; tA[j] == tB[j]; j++) pos++ if (j && j < tA.length && j < tB.length && surrogateHigh(tA.charCodeAt(j - 1)) && surrogateLow(tA.charCodeAt(j))) pos-- return pos } if (childA.content.size || childB.content.size) { let inner = findDiffStart(childA.content, childB.content, pos + 1) if (inner != null) return inner } pos += childA.nodeSize } } export function findDiffEnd(a: Fragment, b: Fragment, posA: number, posB: number): {a: number, b: number} | null { for (let iA = a.childCount, iB = b.childCount;;) { if (iA == 0 || iB == 0) return iA == iB ? null : {a: posA, b: posB} let childA = a.child(--iA), childB = b.child(--iB), size = childA.nodeSize if (childA == childB) { posA -= size; posB -= size continue } if (!childA.sameMarkup(childB)) return {a: posA, b: posB} if (childA.isText && childA.text != childB.text) { let tA = childA.text!, tB = childB.text!, iA = tA.length, iB = tB.length while (iA > 0 && iB > 0 && tA[iA - 1] == tB[iB - 1]) { iA--; iB--; posA--; posB-- } if (iA && iB && iA < tA.length && surrogateHigh(tA.charCodeAt(iA - 1)) && surrogateLow(tA.charCodeAt(iA))) { posA++; posB++ } return {a: posA, b: posB} } if (childA.content.size || childB.content.size) { let inner = findDiffEnd(childA.content, childB.content, posA - 1, posB - 1) if (inner) return inner } posA -= size; posB -= size } } function surrogateLow(ch: number) { return ch >= 0xDC00 && ch < 0xE000 } function surrogateHigh(ch: number) { return ch >= 0xD800 && ch < 0xDC00 } prosemirror-model/src/dom.ts000066400000000000000000000000671525422544500164700ustar00rootroot00000000000000export type DOMNode = InstanceType prosemirror-model/src/fragment.ts000066400000000000000000000243211525422544500175130ustar00rootroot00000000000000import {findDiffStart, findDiffEnd} from "./diff" import {Node, TextNode} from "./node" import {Schema} from "./schema" /// A fragment represents a node's collection of child nodes. /// /// Like nodes, fragments are persistent data structures, and you /// should not mutate them or their content. Rather, you create new /// instances whenever needed. The API tries to make this easy. export class Fragment { /// The size of the fragment, which is the total of the size of /// its content nodes. readonly size: number /// @internal constructor( /// The child nodes in this fragment. readonly content: readonly Node[], size?: number ) { this.size = size || 0 if (size == null) for (let i = 0; i < content.length; i++) this.size += content[i].nodeSize } /// Invoke a callback for all descendant nodes between the given two /// positions (relative to start of this fragment). Doesn't descend /// into a node when the callback returns `false`. nodesBetween(from: number, to: number, f: (node: Node, start: number, parent: Node | null, index: number) => boolean | void, nodeStart = 0, parent?: Node) { for (let i = 0, pos = 0; pos < to; i++) { let child = this.content[i], end = pos + child.nodeSize if (end > from && f(child, nodeStart + pos, parent || null, i) !== false && child.content.size) { let start = pos + 1 child.nodesBetween(Math.max(0, from - start), Math.min(child.content.size, to - start), f, nodeStart + start) } pos = end } } /// Call the given callback for every descendant node. `pos` will be /// relative to the start of the fragment. The callback may return /// `false` to prevent traversal of a given node's children. descendants(f: (node: Node, pos: number, parent: Node | null, index: number) => boolean | void) { this.nodesBetween(0, this.size, f) } /// Extract the text between `from` and `to`. See the same method on /// [`Node`](#model.Node.textBetween). textBetween(from: number, to: number, blockSeparator?: string | null, leafText?: string | null | ((leafNode: Node) => string)) { let text = "", first = true this.nodesBetween(from, to, (node, pos) => { let nodeText = node.isText ? node.text!.slice(Math.max(from, pos) - pos, to - pos) : !node.isLeaf ? "" : leafText ? (typeof leafText === "function" ? leafText(node) : leafText) : node.type.spec.leafText ? node.type.spec.leafText(node) : "" if (node.isBlock && (node.isLeaf && nodeText || node.isTextblock) && blockSeparator) { if (first) first = false else text += blockSeparator } text += nodeText }, 0) return text } /// Create a new fragment containing the combined content of this /// fragment and the other. append(other: Fragment) { if (!other.size) return this if (!this.size) return other let last = this.lastChild!, first = other.firstChild!, content = this.content.slice(), i = 0 if (last.isText && last.sameMarkup(first)) { content[content.length - 1] = (last as TextNode).withText(last.text! + first.text!) i = 1 } for (; i < other.content.length; i++) content.push(other.content[i]) return new Fragment(content, this.size + other.size) } /// Cut out the sub-fragment between the two given positions. cut(from: number, to = this.size) { if (from == 0 && to == this.size) return this let result: Node[] = [], size = 0 if (to > from) for (let i = 0, pos = 0; pos < to; i++) { let child = this.content[i], end = pos + child.nodeSize if (end > from) { if (pos < from || end > to) { if (child.isText) child = child.cut(Math.max(0, from - pos), Math.min(child.text!.length, to - pos)) else child = child.cut(Math.max(0, from - pos - 1), Math.min(child.content.size, to - pos - 1)) } result.push(child) size += child.nodeSize } pos = end } return new Fragment(result, size) } /// @internal cutByIndex(from: number, to: number) { if (from == to) return Fragment.empty if (from == 0 && to == this.content.length) return this return new Fragment(this.content.slice(from, to)) } /// Create a new fragment in which the node at the given index is /// replaced by the given node. replaceChild(index: number, node: Node) { let current = this.content[index] if (current == node) return this let copy = this.content.slice() let size = this.size + node.nodeSize - current.nodeSize copy[index] = node return new Fragment(copy, size) } /// Create a new fragment by prepending the given node to this /// fragment. addToStart(node: Node) { return new Fragment([node].concat(this.content), this.size + node.nodeSize) } /// Create a new fragment by appending the given node to this /// fragment. addToEnd(node: Node) { return new Fragment(this.content.concat(node), this.size + node.nodeSize) } /// Compare this fragment to another one. eq(other: Fragment): boolean { if (this.content.length != other.content.length) return false for (let i = 0; i < this.content.length; i++) if (!this.content[i].eq(other.content[i])) return false return true } /// The first child of the fragment, or `null` if it is empty. get firstChild(): Node | null { return this.content.length ? this.content[0] : null } /// The last child of the fragment, or `null` if it is empty. get lastChild(): Node | null { return this.content.length ? this.content[this.content.length - 1] : null } /// The number of child nodes in this fragment. get childCount() { return this.content.length } /// Get the child node at the given index. Raise an error when the /// index is out of range. child(index: number) { let found = this.content[index] if (!found) throw new RangeError("Index " + index + " out of range for " + this) return found } /// Get the child node at the given index, if it exists. maybeChild(index: number): Node | null { return this.content[index] || null } /// Call `f` for every child node, passing the node, its offset /// into this parent node, and its index. forEach(f: (node: Node, offset: number, index: number) => void) { for (let i = 0, p = 0; i < this.content.length; i++) { let child = this.content[i] f(child, p, i) p += child.nodeSize } } /// Find the first position at which this fragment and another /// fragment differ, or `null` if they are the same. findDiffStart(other: Fragment, pos = 0) { return findDiffStart(this, other, pos) } /// Find the first position, searching from the end, at which this /// fragment and the given fragment differ, or `null` if they are /// the same. Since this position will not be the same in both /// nodes, an object with two separate positions is returned. findDiffEnd(other: Fragment, pos = this.size, otherPos = other.size) { return findDiffEnd(this, other, pos, otherPos) } /// Find the index and inner offset corresponding to a given relative /// position in this fragment. The result object will be reused /// (overwritten) the next time the function is called. @internal findIndex(pos: number): {index: number, offset: number} { if (pos == 0) return retIndex(0, pos) if (pos == this.size) return retIndex(this.content.length, pos) if (pos > this.size || pos < 0) throw new RangeError(`Position ${pos} outside of fragment (${this})`) for (let i = 0, curPos = 0;; i++) { let cur = this.child(i), end = curPos + cur.nodeSize if (end >= pos) { if (end == pos) return retIndex(i + 1, end) return retIndex(i, curPos) } curPos = end } } /// Return a debugging string that describes this fragment. toString(): string { return "<" + this.toStringInner() + ">" } /// @internal toStringInner() { return this.content.join(", ") } /// Create a JSON-serializeable representation of this fragment. toJSON(): any { return this.content.length ? this.content.map(n => n.toJSON()) : null } /// Deserialize a fragment from its JSON representation. static fromJSON(schema: Schema, value: any) { if (!value) return Fragment.empty if (!Array.isArray(value)) throw new RangeError("Invalid input for Fragment.fromJSON") return Fragment.fromArray(value.map(schema.nodeFromJSON)) } /// Build a fragment from an array of nodes. Ensures that adjacent /// text nodes with the same marks are joined together. static fromArray(array: readonly Node[]) { if (!array.length) return Fragment.empty let joined: Node[] | undefined, size = 0 for (let i = 0; i < array.length; i++) { let node = array[i] size += node.nodeSize if (i && node.isText && array[i - 1].sameMarkup(node)) { if (!joined) joined = array.slice(0, i) joined[joined.length - 1] = (node as TextNode) .withText((joined[joined.length - 1] as TextNode).text + (node as TextNode).text) } else if (joined) { joined.push(node) } } return new Fragment(joined || array, size) } /// Create a fragment from something that can be interpreted as a /// set of nodes. For `null`, it returns the empty fragment. For a /// fragment, the fragment itself. For a node or array of nodes, a /// fragment containing those nodes. static from(nodes?: Fragment | Node | readonly Node[] | null) { if (!nodes) return Fragment.empty if (nodes instanceof Fragment) return nodes if (Array.isArray(nodes)) return this.fromArray(nodes) if ((nodes as Node).attrs) return new Fragment([nodes as Node], (nodes as Node).nodeSize) throw new RangeError("Can not convert " + nodes + " to a Fragment" + ((nodes as any).nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : "")) } /// An empty fragment. Intended to be reused whenever a node doesn't /// contain anything (rather than allocating a new empty fragment for /// each leaf node). static empty: Fragment = new Fragment([], 0) } const found = {index: 0, offset: 0} function retIndex(index: number, offset: number) { found.index = index found.offset = offset return found } prosemirror-model/src/from_dom.ts000066400000000000000000001015411525422544500175120ustar00rootroot00000000000000import {Fragment} from "./fragment" import {Slice} from "./replace" import {Mark} from "./mark" import {Node, TextNode} from "./node" import {ContentMatch} from "./content" import {ResolvedPos} from "./resolvedpos" import {Schema, Attrs, NodeType, MarkType} from "./schema" import {DOMNode} from "./dom" /// These are the options recognized by the /// [`parse`](#model.DOMParser.parse) and /// [`parseSlice`](#model.DOMParser.parseSlice) methods. export interface ParseOptions { /// By default, whitespace is collapsed as per HTML's rules. Pass /// `true` to preserve whitespace, but normalize newlines to /// spaces or, if available, [line break replacements](#model.NodeSpec.linebreakReplacement), /// and `"full"` to preserve whitespace entirely. preserveWhitespace?: boolean | "full" /// When given, the parser will, beside parsing the content, /// record the document positions of the given DOM positions. It /// will do so by writing to the objects, adding a `pos` property /// that holds the document position. DOM positions that are not /// in the parsed content will not be written to. findPositions?: {node: DOMNode, offset: number, pos?: number}[] /// The child node index to start parsing from. from?: number /// The child node index to stop parsing at. to?: number /// By default, the content is parsed into the schema's default /// [top node type](#model.Schema.topNodeType). You can pass this /// option to use the type and attributes from a different node /// as the top container. topNode?: Node /// Provide the starting content match that content parsed into the /// top node is matched against. topMatch?: ContentMatch /// A set of additional nodes to count as /// [context](#model.GenericParseRule.context) when parsing, above the /// given [top node](#model.ParseOptions.topNode). context?: ResolvedPos /// @internal ruleFromNode?: (node: DOMNode) => Omit | null /// @internal topOpen?: boolean } /// Fields that may be present in both [tag](#model.TagParseRule) and /// [style](#model.StyleParseRule) parse rules. export interface GenericParseRule { /// Can be used to change the order in which the parse rules in a /// schema are tried. Those with higher priority come first. Rules /// without a priority are counted as having priority 50. This /// property is only meaningful in a schemaโ€”when directly /// constructing a parser, the order of the rule array is used. priority?: number /// By default, when a rule matches an element or style, no further /// rules get a chance to match it. By setting this to `false`, you /// indicate that even when this rule matches, other rules that come /// after it should also run. consuming?: boolean /// When given, restricts this rule to only match when the current /// contextโ€”the parent nodes into which the content is being /// parsedโ€”matches this expression. Should contain one or more node /// names or node group names followed by single or double slashes. /// For example `"paragraph/"` means the rule only matches when the /// parent node is a paragraph, `"blockquote/paragraph/"` restricts /// it to be in a paragraph that is inside a blockquote, and /// `"section//"` matches any position inside a sectionโ€”a double /// slash matches any sequence of ancestor nodes. To allow multiple /// different contexts, they can be separated by a pipe (`|`) /// character, as in `"blockquote/|list_item/"`. context?: string /// The name of the mark type to wrap the matched content in. mark?: string /// When true, ignore content that matches this rule. Any ``, /// `