prosemirror-transform
Advanced tools
Comparing version 0.22.2 to 0.23.0
@@ -16,3 +16,3 @@ # How to contribute | ||
[GitHub issue tracker](http://github.com/prosemirror/prosemirror/issues). | ||
Before reporting a bug, read these pointers. | ||
Before reporting a bug, please read these pointers. | ||
@@ -26,6 +26,7 @@ - The issue tracker is for *bugs*, not requests for help. Questions | ||
- 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. We can not | ||
fix something that we can not observe. | ||
- 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. | ||
@@ -50,4 +51,4 @@ - A great way to make it easy to reproduce your problem, if it can not | ||
- Follow the code style of the rest of the project (see below). Run | ||
`npm run lint` (in the main repository checkout) that the linter is | ||
happy. | ||
`npm run lint` (in the main repository checkout) to make sure that | ||
the linter is happy. | ||
@@ -54,0 +55,0 @@ - If your changes are easy to test or likely to regress, add tests in |
{ | ||
"name": "prosemirror-transform", | ||
"version": "0.22.2", | ||
"version": "0.23.0", | ||
"description": "ProseMirror document transformations", | ||
@@ -19,16 +19,17 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"prosemirror-model": "^0.22.0" | ||
"prosemirror-model": "^0.23.0" | ||
}, | ||
"devDependencies": { | ||
"buble": "^0.15.1", | ||
"mocha": "^3.0.2", | ||
"ist": "^1.0.0", | ||
"rimraf": "^2.5.4", | ||
"prosemirror-test-builder": "^0.22.0" | ||
"prosemirror-test-builder": "^0.23.0", | ||
"rollup": "^0.49.0", | ||
"rollup-plugin-buble": "^0.15.0" | ||
}, | ||
"scripts": { | ||
"test": "mocha test/test-*.js", | ||
"build": "rimraf dist && buble -i src -o dist", | ||
"build": "rollup -c", | ||
"watch": "rollup -c -w", | ||
"prepare": "npm run build" | ||
} | ||
} |
@@ -9,4 +9,4 @@ # prosemirror-transform | ||
This [module](http://prosemirror.net/ref.html#transform) implements | ||
document [transforms](http://prosemirror.net/guide/transform.html), | ||
This [module](http://prosemirror.net/docs/ref/#transform) implements | ||
document [transforms](http://prosemirror\.net/docs/guide/#transform), | ||
which are used by the editor to treat changes as first-class values, | ||
@@ -16,9 +16,5 @@ which can be saved, shared, and reasoned about. | ||
The [project page](http://prosemirror.net) has more information, a | ||
number of [demos](http://prosemirror.net/#demos) and the | ||
[documentation](http://prosemirror.net/docs.html). | ||
number of [examples](http://prosemirror.net/examples/) and the | ||
[documentation](http://prosemirror.net/docs/). | ||
**NOTE:** This project is in *BETA* stage. It isn't thoroughly tested, | ||
and the API might still change across `0.x` releases. You are welcome | ||
to use it, but don't expect it to be very stable yet. | ||
This code is released under an | ||
@@ -25,0 +21,0 @@ [MIT license](https://github.com/prosemirror/prosemirror/tree/master/LICENSE). |
@@ -1,9 +0,8 @@ | ||
;({Transform: exports.Transform, TransformError: exports.TransformError} = require("./transform")) | ||
;({Step: exports.Step, StepResult: exports.StepResult} = require("./step")) | ||
;({joinPoint: exports.joinPoint, canJoin: exports.canJoin, canSplit: exports.canSplit, | ||
insertPoint: exports.insertPoint, liftTarget: exports.liftTarget, findWrapping: exports.findWrapping} = require("./structure")) | ||
;({StepMap: exports.StepMap, MapResult: exports.MapResult, Mapping: exports.Mapping} = require("./map")) | ||
;({AddMarkStep: exports.AddMarkStep, RemoveMarkStep: exports.RemoveMarkStep} = require("./mark_step")) | ||
;({ReplaceStep: exports.ReplaceStep, ReplaceAroundStep: exports.ReplaceAroundStep} = require("./replace_step")) | ||
require("./mark") | ||
;({replaceStep: exports.replaceStep} = require("./replace")) | ||
export {Transform, TransformError} from "./transform" | ||
export {Step, StepResult} from "./step" | ||
export {joinPoint, canJoin, canSplit, insertPoint, liftTarget, findWrapping} from "./structure" | ||
export {StepMap, MapResult, Mapping} from "./map" | ||
export {AddMarkStep, RemoveMarkStep} from "./mark_step" | ||
export {ReplaceStep, ReplaceAroundStep} from "./replace_step" | ||
import "./mark" | ||
export {replaceStep} from "./replace" |
// Mappable:: interface | ||
// There are several things that positions can be mapped through. | ||
// We'll denote those as 'mappable'. | ||
// Such objects conform to this interface. | ||
// | ||
@@ -9,4 +9,3 @@ // map:: (pos: number, assoc: ?number) → number | ||
// position is associated, which determines in which direction to | ||
// move when a chunk of content is inserted at the mapped position, | ||
// and when to consider the position to be deleted. | ||
// move when a chunk of content is inserted at the mapped position. | ||
// | ||
@@ -17,7 +16,9 @@ // mapResult:: (pos: number, assoc: ?number) → MapResult | ||
// you whether the position was deleted (completely enclosed in a | ||
// replaced range) during the mapping. | ||
// replaced range) during the mapping. When content on only one side | ||
// is deleted, the position itself is only considered deleted when | ||
// `assoc` points in the direction of the deleted content. | ||
// Recovery values encode a range index and an offset. They are | ||
// represented as numbers, because tons of them will be created when | ||
// mapping, for example, a large number of marked ranges. The number's | ||
// mapping, for example, a large number of decorations. The number's | ||
// lower 16 bits provide the index, the remaining bits the offset. | ||
@@ -39,3 +40,3 @@ // | ||
// information. | ||
class MapResult { | ||
export class MapResult { | ||
constructor(pos, deleted = false, recover = null) { | ||
@@ -50,9 +51,9 @@ // :: number The mapped version of the position. | ||
} | ||
exports.MapResult = MapResult | ||
// ::- A map describing the deletions and insertions made by a step, | ||
// which can be used to find the correspondence between positions in | ||
// the pre-step version of a document and the same position in the | ||
// post-step version. This class implements [`Mappable`](#transform.Mappable). | ||
class StepMap { | ||
// :: class extends Mappable | ||
// A map describing the deletions and insertions made by a step, which | ||
// can be used to find the correspondence between positions in the | ||
// pre-step version of a document and the same position in the | ||
// post-step version. | ||
export class StepMap { | ||
// :: ([number]) | ||
@@ -74,14 +75,7 @@ // Create a position map. The modifications to the document are | ||
// :: (number, ?number) → MapResult | ||
// Map the given position through this map. The `assoc` parameter can | ||
// be used to control what happens when the transform inserted | ||
// content at (or around) this position—if `assoc` is negative, the a | ||
// position before the inserted content will be returned, if it is | ||
// positive, a position after the insertion is returned. | ||
mapResult(pos, assoc) { return this._map(pos, assoc, false) } | ||
// : (number, ?number) → MapResult | ||
mapResult(pos, assoc = 1) { return this._map(pos, assoc, false) } | ||
// :: (number, ?number) → number | ||
// Map the given position through this map, returning only the | ||
// mapped position. | ||
map(pos, assoc) { return this._map(pos, assoc, true) } | ||
// : (number, ?number) → number | ||
map(pos, assoc = 1) { return this._map(pos, assoc, true) } | ||
@@ -120,3 +114,3 @@ _map(pos, assoc, simple) { | ||
// :: ((oldStart: number, oldEnd: number, newStart: number, newEnd: number)) | ||
// Calls the given function on each of the changed ranges denoted by | ||
// Calls the given function on each of the changed ranges included in | ||
// this map. | ||
@@ -143,14 +137,22 @@ forEach(f) { | ||
} | ||
// :: (n: number) → StepMap | ||
// Create a map that moves all positions by offset `n` (which may be | ||
// negative). This can be useful when applying steps meant for a | ||
// sub-document to a larger document, or vice-versa. | ||
static offset(n) { | ||
return n == 0 ? StepMap.empty : new StepMap(n < 0 ? [0, -n, 0] : [0, 0, n]) | ||
} | ||
} | ||
exports.StepMap = StepMap | ||
StepMap.empty = new StepMap([]) | ||
// ::- A mapping represents a pipeline of zero or more [step | ||
// :: class extends Mappable | ||
// A mapping represents a pipeline of zero or more [step | ||
// maps](#transform.StepMap). It has special provisions for losslessly | ||
// handling mapping positions through a series of steps in which some | ||
// steps are inverted versions of earlier steps. (This comes up when | ||
// ‘rebasing’ steps for collaboration or history management.) This | ||
// class implements [`Mappable`](#transform.Mappable). | ||
class Mapping { | ||
// ‘[rebasing](/docs/guide/#transform.rebasing)’ steps for | ||
// collaboration or history management.) | ||
export class Mapping { | ||
// :: (?[StepMap]) | ||
@@ -167,3 +169,3 @@ // Create a new mapping with the given position maps. | ||
// :: number | ||
// The end positions in the `maps` array. | ||
// The end position in the `maps` array. | ||
this.to = to == null ? this.maps.length : to | ||
@@ -229,5 +231,5 @@ this.mirror = mirror | ||
// :: (number, ?number) → number | ||
// : (number, ?number) → number | ||
// Map a position through this mapping. | ||
map(pos, assoc) { | ||
map(pos, assoc = 1) { | ||
if (this.mirror) return this._map(pos, assoc, true) | ||
@@ -239,6 +241,6 @@ for (let i = this.from; i < this.to; i++) | ||
// :: (number, ?number) → MapResult | ||
// : (number, ?number) → MapResult | ||
// Map a position through this mapping, returning a mapping | ||
// result. | ||
mapResult(pos, assoc) { return this._map(pos, assoc, false) } | ||
mapResult(pos, assoc = 1) { return this._map(pos, assoc, false) } | ||
@@ -276,2 +278,1 @@ _map(pos, assoc, simple) { | ||
} | ||
exports.Mapping = Mapping |
@@ -1,3 +0,3 @@ | ||
const {Fragment, Slice} = require("prosemirror-model") | ||
const {Step, StepResult} = require("./step") | ||
import {Fragment, Slice} from "prosemirror-model" | ||
import {Step, StepResult} from "./step" | ||
@@ -16,3 +16,3 @@ function mapFragment(fragment, f, parent) { | ||
// ::- Add a mark to all inline content between two positions. | ||
class AddMarkStep extends Step { | ||
export class AddMarkStep extends Step { | ||
// :: (number, number, Mark) | ||
@@ -29,4 +29,4 @@ constructor(from, to, mark) { | ||
let parent = $from.node($from.sharedDepth(this.to)) | ||
let slice = new Slice(mapFragment(oldSlice.content, (node, parent, index) => { | ||
if (!parent.contentMatchAt(index).allowsMark(this.mark.type)) return node | ||
let slice = new Slice(mapFragment(oldSlice.content, (node, parent) => { | ||
if (!parent.type.allowsMarkType(this.mark.type)) return node | ||
return node.mark(this.mark.addToSet(node.marks)) | ||
@@ -55,4 +55,5 @@ }, parent), oldSlice.openStart, oldSlice.openEnd) | ||
offset(n) { | ||
return new AddMarkStep(this.from + n, this.to + n, this.mark) | ||
toJSON() { | ||
return {stepType: "addMark", mark: this.mark.toJSON(), | ||
from: this.from, to: this.to} | ||
} | ||
@@ -64,3 +65,2 @@ | ||
} | ||
exports.AddMarkStep = AddMarkStep | ||
@@ -70,3 +70,3 @@ Step.jsonID("addMark", AddMarkStep) | ||
// ::- Remove a mark from all inline content between two positions. | ||
class RemoveMarkStep extends Step { | ||
export class RemoveMarkStep extends Step { | ||
// :: (number, number, Mark) | ||
@@ -106,4 +106,5 @@ constructor(from, to, mark) { | ||
offset(n) { | ||
return new RemoveMarkStep(this.from + n, this.to + n, this.mark) | ||
toJSON() { | ||
return {stepType: "removeMark", mark: this.mark.toJSON(), | ||
from: this.from, to: this.to} | ||
} | ||
@@ -115,4 +116,3 @@ | ||
} | ||
exports.RemoveMarkStep = RemoveMarkStep | ||
Step.jsonID("removeMark", RemoveMarkStep) |
@@ -1,6 +0,6 @@ | ||
const {MarkType, Slice, Fragment} = require("prosemirror-model") | ||
import {MarkType, Slice, Fragment} from "prosemirror-model" | ||
const {Transform} = require("./transform") | ||
const {AddMarkStep, RemoveMarkStep} = require("./mark_step") | ||
const {ReplaceStep} = require("./replace_step") | ||
import {Transform} from "./transform" | ||
import {AddMarkStep, RemoveMarkStep} from "./mark_step" | ||
import {ReplaceStep} from "./replace_step" | ||
@@ -11,6 +11,6 @@ // :: (number, number, Mark) → this | ||
let removed = [], added = [], removing = null, adding = null | ||
this.doc.nodesBetween(from, to, (node, pos, parent, index) => { | ||
this.doc.nodesBetween(from, to, (node, pos, parent) => { | ||
if (!node.isInline) return | ||
let marks = node.marks | ||
if (!mark.isInSet(marks) && parent.contentMatchAt(index).allowsMark(mark.type)) { | ||
if (!mark.isInSet(marks) && parent.type.allowsMarkType(mark.type)) { | ||
let start = Math.max(pos, from), end = Math.min(pos + node.nodeSize, to) | ||
@@ -41,4 +41,6 @@ let newSet = mark.addToSet(marks) | ||
// :: (number, number, ?union<Mark, MarkType>) → this | ||
// Remove the given mark, or all marks of the given type, from inline | ||
// nodes between `from` and `to`. | ||
// Remove marks from inline nodes between `from` and `to`. When `mark` | ||
// is a single mark, remove precisely that mark. When it is a mark type, | ||
// remove all marks of that type. When it is null, remove all marks of | ||
// any type. | ||
Transform.prototype.removeMark = function(from, to, mark = null) { | ||
@@ -79,20 +81,8 @@ let matched = [], step = 0 | ||
// :: (number, number) → this | ||
// Remove all marks and non-text inline nodes from the given range. | ||
Transform.prototype.clearMarkup = function(from, to) { | ||
let delSteps = [] // Must be accumulated and applied in inverse order | ||
this.doc.nodesBetween(from, to, (node, pos) => { | ||
if (!node.isInline) return | ||
if (!node.type.isText) { | ||
delSteps.push(new ReplaceStep(pos, pos + node.nodeSize, Slice.empty)) | ||
return | ||
} | ||
for (let i = 0; i < node.marks.length; i++) | ||
this.step(new RemoveMarkStep(Math.max(pos, from), Math.min(pos + node.nodeSize, to), node.marks[i])) | ||
}) | ||
for (let i = delSteps.length - 1; i >= 0; i--) this.step(delSteps[i]) | ||
return this | ||
} | ||
Transform.prototype.clearNonMatching = function(pos, match) { | ||
// :: (number, NodeType, ?ContentMatch) → this | ||
// Removes all marks and nodes from the content of the node at `pos` | ||
// that don't match the given new parent node type. Accepts an | ||
// optional starting [content match](#model.ContentMatch) as third | ||
// argument. | ||
Transform.prototype.clearIncompatible = function(pos, parentType, match = parentType.contentMatch) { | ||
let node = this.doc.nodeAt(pos) | ||
@@ -107,3 +97,3 @@ let delSteps = [], cur = pos + 1 | ||
match = allowed | ||
for (let j = 0; j < child.marks.length; j++) if (!match.allowsMark(child.marks[j])) | ||
for (let j = 0; j < child.marks.length; j++) if (!parentType.allowsMarkType(child.marks[j].type)) | ||
this.step(new RemoveMarkStep(cur, end, child.marks[j])) | ||
@@ -113,3 +103,3 @@ } | ||
} | ||
if (!match.validEnd()) { | ||
if (!match.validEnd) { | ||
let fill = match.fillBefore(Fragment.empty, true) | ||
@@ -116,0 +106,0 @@ this.replace(cur, cur, new Slice(fill, 0, 0)) |
@@ -1,3 +0,4 @@ | ||
This module defines a way to transform documents. You can read more | ||
about transformations in [this guide](/docs/guides/transform/). | ||
This module defines a way of modifying documents that allows changes | ||
to be recorded, replayed, and reordered. You can read more about | ||
transformations in [the guide](/docs/guide/#transform). | ||
@@ -26,4 +27,5 @@ ### Steps | ||
Mapping positions from one document to another by running through the | ||
[replacements](#transform.StepMap) produced by steps is a fundamental | ||
operation in ProseMirror. | ||
[step maps](#transform.StepMap) produced by steps is an important | ||
operation in ProseMirror. It is used, for example, for updating the | ||
selection when the document changes. | ||
@@ -35,8 +37,8 @@ @Mappable | ||
### Transform Helpers | ||
### Document transforms | ||
Because you often need to collect a number of steps together to effect | ||
a composite change, ProseMirror provides an abstraction to make this | ||
easy. A value of this class is also the payload in the | ||
[transform action](#state.TransformAction). | ||
easy. [State transactions](#state.Transaction) are a subclass of | ||
transforms. | ||
@@ -43,0 +45,0 @@ @Transform |
@@ -1,8 +0,8 @@ | ||
const {Slice} = require("prosemirror-model") | ||
import {Slice} from "prosemirror-model" | ||
const {Step, StepResult} = require("./step") | ||
const {StepMap} = require("./map") | ||
import {Step, StepResult} from "./step" | ||
import {StepMap} from "./map" | ||
// ::- Replace a part of the document with a slice of new content. | ||
class ReplaceStep extends Step { | ||
export class ReplaceStep extends Step { | ||
// :: (number, number, Slice, ?bool) | ||
@@ -67,6 +67,2 @@ // The given `slice` should fit the 'gap' between `from` and | ||
offset(n) { | ||
return new ReplaceStep(this.from + n, this.to + n, this.slice, this.structure) | ||
} | ||
static fromJSON(schema, json) { | ||
@@ -76,3 +72,2 @@ return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure) | ||
} | ||
exports.ReplaceStep = ReplaceStep | ||
@@ -84,8 +79,8 @@ Step.jsonID("replace", ReplaceStep) | ||
// slice. | ||
class ReplaceAroundStep extends Step { | ||
export class ReplaceAroundStep extends Step { | ||
// :: (number, number, number, number, Slice, number, ?bool) | ||
// Create a replace-wrap step with the given range and gap. `insert` | ||
// should be the point in the slice into which the gap should be | ||
// moved. `structure` has the same meaning as it has in the | ||
// [`ReplaceStep`](#transform.ReplaceStep) class. | ||
// Create a replace-around step with the given range and gap. | ||
// `insert` should be the point in the slice into which the content | ||
// of the gap should be moved. `structure` has the same meaning as | ||
// it has in the [`ReplaceStep`](#transform.ReplaceStep) class. | ||
constructor(from, to, gapFrom, gapTo, slice, insert, structure) { | ||
@@ -135,14 +130,10 @@ super() | ||
static toJSON() { | ||
toJSON() { | ||
let json = {stepType: "replaceAround", from: this.from, to: this.to, | ||
gapFrom: this.gapFrom, gapTo: this.gapTo, slice: this.slice.toJSON()} | ||
gapFrom: this.gapFrom, gapTo: this.gapTo, insert: this.insert} | ||
if (this.slice.size) json.slice = this.slice.toJSON() | ||
if (this.structure) json.structure = true | ||
return true | ||
return json | ||
} | ||
offset(n) { | ||
return new ReplaceAroundStep(this.from + n, this.to + n, this.gapFrom + n, this.gapTo + n, | ||
this.slice, this.insert, this.structure) | ||
} | ||
static fromJSON(schema, json) { | ||
@@ -153,3 +144,2 @@ return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo, | ||
} | ||
exports.ReplaceAroundStep = ReplaceAroundStep | ||
@@ -156,0 +146,0 @@ Step.jsonID("replaceAround", ReplaceAroundStep) |
@@ -1,146 +0,13 @@ | ||
const {Fragment, Slice} = require("prosemirror-model") | ||
import {Fragment, Slice, Mark} from "prosemirror-model" | ||
const {ReplaceStep, ReplaceAroundStep} = require("./replace_step") | ||
const {Transform} = require("./transform") | ||
const {insertPoint} = require("./structure") | ||
import {ReplaceStep, ReplaceAroundStep} from "./replace_step" | ||
import {Transform} from "./transform" | ||
import {insertPoint} from "./structure" | ||
// :: (number, number, Slice) → this | ||
// Replace a range of the document with a given slice, using `from`, | ||
// `to`, and the slice's [`openStart`](#model.Slice.openStart) property | ||
// as hints, rather than fixed start and end points. This method may | ||
// grow the replaced area or close open nodes in the slice in order to | ||
// get a fit that is more in line with WYSIWYG expectations, by | ||
// dropping fully covered parent nodes of the replaced region when | ||
// they are marked [non-defining](#model.NodeSpec.defining), or | ||
// including an open parent node from the slice that _is_ marked as | ||
// [defining](#model.NodeSpec.defining). | ||
// | ||
// This is the method, for example, to handle paste. The similar | ||
// [`replace`](#transform.Transform.replace) method is a more | ||
// primitive tool which will _not_ move the start and end of its given | ||
// range, and is useful in situations where you need more precise | ||
// control over what happens. | ||
Transform.prototype.replaceRange = function(from, to, slice) { | ||
if (!slice.size) return this.deleteRange(from, to) | ||
let $from = this.doc.resolve(from), $to = this.doc.resolve(to) | ||
if (fitsTrivially($from, $to, slice)) | ||
return this.step(new ReplaceStep(from, to, slice)) | ||
let canExpand = coveredDepths($from, this.doc.resolve(to)), preferredExpand = 0 | ||
if (canExpand[canExpand.length - 1] == 0) canExpand.pop() | ||
canExpand.unshift($from.depth + 1) | ||
for (let d = $from.depth; d > 0; d--) { | ||
if ($from.node(d).type.spec.defining) break | ||
let found = canExpand.indexOf(d, 1) | ||
if (found > -1) preferredExpand = found | ||
} | ||
let leftNodes = [], preferredDepth = slice.openStart | ||
for (let content = slice.content, i = 0;; i++) { | ||
let node = content.firstChild | ||
leftNodes.push(node) | ||
if (i == slice.openStart) break | ||
content = node.content | ||
} | ||
// Back up if the node directly above openStart, or the node above | ||
// that separated only by a non-defining textblock node, is defining. | ||
if (preferredDepth > 0 && leftNodes[preferredDepth - 1].type.spec.defining) | ||
preferredDepth -= 1 | ||
else if (preferredDepth >= 2 && leftNodes[preferredDepth - 1].isTextblock && leftNodes[preferredDepth - 2].type.spec.defining) | ||
preferredDepth -= 2 | ||
for (let j = slice.openStart; j >= 0; j--) { | ||
let openDepth = (j + preferredDepth + 1) % (slice.openStart + 1) | ||
let insert = leftNodes[openDepth] | ||
if (!insert) continue | ||
for (let i = 0; i < canExpand.length; i++) { | ||
// Loop over possible expansion levels, starting with the | ||
// preferred one | ||
let expandDepth = canExpand[(i + preferredExpand) % canExpand.length] | ||
let parent = $from.node(expandDepth - 1), index = $from.index(expandDepth - 1) | ||
if (parent.canReplaceWith(index, index, insert.type, insert.attrs, insert.marks)) | ||
return this.replace($from.before(expandDepth), expandDepth > $from.depth ? to : $to.after(expandDepth), | ||
new Slice(closeFragment(slice.content, 0, slice.openStart, openDepth), | ||
openDepth, slice.openEnd)) | ||
} | ||
} | ||
return this.replace(from, to, slice) | ||
} | ||
function closeFragment(fragment, depth, oldOpen, newOpen, parent) { | ||
if (depth < oldOpen) { | ||
let first = fragment.firstChild | ||
fragment = fragment.replaceChild(0, first.copy(closeFragment(first.content, depth + 1, oldOpen, newOpen, first))) | ||
} | ||
if (depth > newOpen) | ||
fragment = parent.contentMatchAt(0).fillBefore(fragment).append(fragment) | ||
return fragment | ||
} | ||
// :: (number, number, Node) → this | ||
// Replace the given range with a node, but use `from` and `to` as | ||
// hints, rather than precise positions. When from and to are the same | ||
// and are at the start or end of a parent node in which the given | ||
// node doesn't fit, this method may _move_ them out towards a parent | ||
// that does allow the given node to be placed. When the given range | ||
// completely covers a parent node, this method may completely replace | ||
// that parent node. | ||
Transform.prototype.replaceRangeWith = function(from, to, node) { | ||
if (!node.isInline && from == to && this.doc.resolve(from).parent.content.size) { | ||
let point = insertPoint(this.doc, from, node.type, node.attrs) | ||
if (point != null) from = to = point | ||
} | ||
return this.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0)) | ||
} | ||
// :: (number, number) → this | ||
// Delete the given range, expanding it to cover fully covered | ||
// parent nodes until a valid replace is found. | ||
Transform.prototype.deleteRange = function(from, to) { | ||
let $from = this.doc.resolve(from), $to = this.doc.resolve(to) | ||
let covered = coveredDepths($from, $to) | ||
for (let i = 0; i < covered.length; i++) { | ||
let depth = covered[i], last = i == covered.length - 1 | ||
if ((last && depth == 0) || $from.node(depth).contentMatchAt(0).validEnd()) { | ||
from = $from.start(depth) | ||
to = $to.end(depth) | ||
break | ||
} | ||
if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) { | ||
from = $from.before(depth) | ||
to = $to.after(depth) | ||
break | ||
} | ||
} | ||
return this.delete(from, to) | ||
} | ||
// : (ResolvedPos, ResolvedPos) → [number] | ||
// Returns an array of all depths for which $from - $to spans the | ||
// whole content of the nodes at that depth. | ||
function coveredDepths($from, $to) { | ||
let result = [], minDepth = Math.min($from.depth, $to.depth) | ||
for (let d = minDepth; d >= 0; d--) { | ||
let start = $from.start(d) | ||
if (start < $from.pos - ($from.depth - d) || | ||
$to.end(d) > $to.pos + ($to.depth - d) || | ||
$from.node(d).type.spec.isolating || | ||
$to.node(d).type.spec.isolating) break | ||
if (start == $to.start(d)) result.push(d) | ||
} | ||
return result | ||
} | ||
// :: (number, number) → this | ||
// Delete the content between the given positions. | ||
Transform.prototype.delete = function(from, to) { | ||
return this.replace(from, to, Slice.empty) | ||
} | ||
// :: (Node, number, ?number, ?Slice) → ?Step | ||
// "Fit" a slice into a given position in the document, producing a | ||
// [step](#transform.Step) that inserts it. | ||
function replaceStep(doc, from, to = from, slice = Slice.empty) { | ||
// ‘Fit’ a slice into a given position in the document, producing a | ||
// [step](#transform.Step) that inserts it. Will return null if | ||
// there's no meaningful way to insert the slice here, or inserting it | ||
// would be a no-op (an empty slice over an empty range). | ||
export function replaceStep(doc, from, to = from, slice = Slice.empty) { | ||
if (from == to && !slice.size) return null | ||
@@ -165,3 +32,2 @@ | ||
} | ||
exports.replaceStep = replaceStep | ||
@@ -184,2 +50,8 @@ // :: (number, ?number, ?Slice) → this | ||
// :: (number, number) → this | ||
// Delete the content between the given positions. | ||
Transform.prototype.delete = function(from, to) { | ||
return this.replace(from, to, Slice.empty) | ||
} | ||
// :: (number, union<Fragment, Node, [Node]>) → this | ||
@@ -248,3 +120,3 @@ // Insert the given content at the given position. | ||
if (openEnd > 0) | ||
match = match.matchNode(count == 1 && openStart > 0 ? $from.node(depth + 1) : content.lastChild) | ||
match = match.matchType((count == 1 && openStart > 0 ? $from.node(depth + 1) : content.lastChild).type) | ||
@@ -330,3 +202,3 @@ // If we're here, the next level can't be joined, so we see what | ||
match = match.matchFragment($to.parent.content, $to.index()) | ||
return match && match.validEnd() | ||
return match && match.validEnd | ||
} | ||
@@ -373,15 +245,14 @@ | ||
// Get the components of the node at this level | ||
let curType, curAttrs, curFragment | ||
let curType, curAttrs, curMarks, curFragment | ||
if (dSlice >= 0) { | ||
if (dSlice > 0) { // Inside slice | ||
;({type: curType, attrs: curAttrs, content: curFragment} = nodeLeft(slice.content, dSlice)) | ||
;({type: curType, attrs: curAttrs, content: curFragment, marks: curMarks} = nodeLeft(slice.content, dSlice)) | ||
} else if (dSlice == 0) { // Top of slice | ||
curFragment = slice.content | ||
curMarks = Mark.none | ||
} | ||
if (dSlice < slice.openStart) curFragment = curFragment.cut(curFragment.firstChild.nodeSize) | ||
if (dSlice < slice.openStart) curFragment = curFragment.cutByIndex(1, curFragment.childCount) | ||
} else { // Outside slice, in generated wrappers (see below) | ||
curFragment = Fragment.empty | ||
let parent = parents[parents.length + dSlice - 1] | ||
curType = parent.type | ||
curAttrs = parent.attrs | ||
;({type: curType, attrs: curAttrs, marks: curMarks} = parents[parents.length + dSlice - 1]) | ||
} | ||
@@ -402,6 +273,6 @@ // If the last iteration left unplaced content, include it in the fragment | ||
// If there was a fit, store it, and consider this content placed | ||
if (found.fragment.size > 0) placed[found.depth] = { | ||
content: found.fragment, | ||
openEnd: endOfContent(slice, dSlice) ? slice.openEnd - dSlice : 0, | ||
depth: found.depth | ||
if (found.fragment.size > 0) { | ||
let openEnd = endOfContent(slice, dSlice) ? slice.openEnd - dSlice : 0 | ||
if (placed[found.depth] && curFragment.size == 0) openEnd = placed[found.depth].openEnd | ||
placed[found.depth] = {content: found.fragment, openEnd, depth: found.depth} | ||
} | ||
@@ -417,3 +288,3 @@ // If that was the last of the content, we're done | ||
// Try to find a wrapping that makes its first child fit in the top node. | ||
let wrap = top.contentMatchAt($from.index(0)).findWrappingFor(curFragment.firstChild) | ||
let wrap = top.contentMatchAt($from.index(0)).findWrapping(curFragment.firstChild.type) | ||
// If no such thing exists, give up. | ||
@@ -423,10 +294,11 @@ if (!wrap || wrap.length == 0) break | ||
// Check that the fragment actually fits in the wrapping. | ||
if (!last.type.contentExpr.matches(last.attrs, curFragment)) break | ||
if (!last.validContent(curFragment)) break | ||
// Store the result for subsequent iterations. | ||
parents = [{type: top.type, attrs: top.attrs}].concat(wrap) | ||
;({type: curType, attrs: curAttrs} = last) | ||
curType = last | ||
curAttrs = null | ||
} | ||
if (curFragment.size) { | ||
curFragment = curType.contentExpr.start(curAttrs).fillBefore(curFragment, true).append(curFragment) | ||
unplaced = curType.create(curAttrs, curFragment) | ||
curFragment = curType.contentMatch.fillBefore(curFragment, true).append(curFragment) | ||
unplaced = curType.create(curAttrs, curFragment, curMarks) | ||
} else { | ||
@@ -454,3 +326,3 @@ unplaced = null | ||
for (let d = start; d >= 0; d--) { | ||
let startMatch = $from.node(d).contentMatchAt($from.indexAfter(d)) | ||
let parent = $from.node(d), startMatch = parent.contentMatchAt($from.indexAfter(d)) | ||
let existing = placed[d] | ||
@@ -461,3 +333,3 @@ if (existing) startMatch = startMatch.matchFragment(existing.content) | ||
if (hasMarks) { | ||
let stripped = matchStrippingMarks(startMatch, fragment) | ||
let stripped = matchStrippingMarks(parent.type, startMatch, fragment) | ||
if (stripped) return {depth: d, fragment: existing ? existing.content.append(stripped) : stripped} | ||
@@ -468,9 +340,9 @@ } | ||
function matchStrippingMarks(match, fragment) { | ||
function matchStrippingMarks(type, match, fragment) { | ||
let newNodes = [] | ||
for (let i = 0; i < fragment.childCount; i++) { | ||
let node = fragment.child(i), stripped = node.mark(node.marks.filter(m => match.allowsMark(m.type))) | ||
match = match.matchNode(stripped) | ||
let node = fragment.child(i) | ||
match = match.matchType(node.type) | ||
if (!match) return null | ||
newNodes.push(stripped) | ||
newNodes.push(node.mark(type.allowedMarks(node.marks))) | ||
} | ||
@@ -483,7 +355,151 @@ return Fragment.from(newNodes) | ||
for (; dFrom > dFound; dFrom--) { | ||
let here = $from.node(dFrom).contentMatchAt($from.indexAfter(dFrom)) | ||
for (let d = dSlice - 1; d >= 0; d--) | ||
if (here.matchNode(nodeLeft(slice.content, d))) return true | ||
let parent = $from.node(dFrom), here = parent.contentMatchAt($from.indexAfter(dFrom)) | ||
for (let d = dSlice - 1; d >= 0; d--) { | ||
let node = nodeLeft(slice.content, d) | ||
if (here.matchType(node.type) && parent.type.allowsMarks(node.marks)) return true | ||
} | ||
} | ||
return false | ||
} | ||
// :: (number, number, Slice) → this | ||
// Replace a range of the document with a given slice, using `from`, | ||
// `to`, and the slice's [`openStart`](#model.Slice.openStart) property | ||
// as hints, rather than fixed start and end points. This method may | ||
// grow the replaced area or close open nodes in the slice in order to | ||
// get a fit that is more in line with WYSIWYG expectations, by | ||
// dropping fully covered parent nodes of the replaced region when | ||
// they are marked [non-defining](#model.NodeSpec.defining), or | ||
// including an open parent node from the slice that _is_ marked as | ||
// [defining](#model.NodeSpec.defining). | ||
// | ||
// This is the method, for example, to handle paste. The similar | ||
// [`replace`](#transform.Transform.replace) method is a more | ||
// primitive tool which will _not_ move the start and end of its given | ||
// range, and is useful in situations where you need more precise | ||
// control over what happens. | ||
Transform.prototype.replaceRange = function(from, to, slice) { | ||
if (!slice.size) return this.deleteRange(from, to) | ||
let $from = this.doc.resolve(from), $to = this.doc.resolve(to) | ||
if (fitsTrivially($from, $to, slice)) | ||
return this.step(new ReplaceStep(from, to, slice)) | ||
let targetDepths = coveredDepths($from, this.doc.resolve(to)) | ||
// Can't replace the whole document, so remove 0 if it's present | ||
if (targetDepths[targetDepths.length - 1] == 0) targetDepths.pop() | ||
// Negative numbers represent not expansion over the whole node at | ||
// that depth, but replacing from $from.before(-D) to $to.pos. | ||
let preferredTarget = -($from.depth + 1) | ||
targetDepths.unshift(preferredTarget) | ||
// This loop picks a preferred target depth, if one of the covering | ||
// depths is not outside of a defining node, and adds negative | ||
// depths for any depth that has $from at its start and does not | ||
// cross a defining node. | ||
for (let d = $from.depth, pos = $from.pos - 1; d > 0; d--, pos--) { | ||
let spec = $from.node(d).type.spec | ||
if (spec.defining || spec.isolating) break | ||
if (targetDepths.indexOf(d) > -1) preferredTarget = d | ||
else if ($from.before(d) == pos) targetDepths.splice(1, 0, -d) | ||
} | ||
let leftNodes = [], preferredDepth = slice.openStart | ||
for (let content = slice.content, i = 0;; i++) { | ||
let node = content.firstChild | ||
leftNodes.push(node) | ||
if (i == slice.openStart) break | ||
content = node.content | ||
} | ||
// Back up if the node directly above openStart, or the node above | ||
// that separated only by a non-defining textblock node, is defining. | ||
if (preferredDepth > 0 && leftNodes[preferredDepth - 1].type.spec.defining) | ||
preferredDepth -= 1 | ||
else if (preferredDepth >= 2 && leftNodes[preferredDepth - 1].isTextblock && leftNodes[preferredDepth - 2].type.spec.defining) | ||
preferredDepth -= 2 | ||
// Try to fit each possible depth of the slice into each possible | ||
// target depth, starting with the preferred depths. | ||
let preferredTargetIndex = targetDepths.indexOf(preferredTarget) | ||
for (let j = slice.openStart; j >= 0; j--) { | ||
let openDepth = (j + preferredDepth + 1) % (slice.openStart + 1) | ||
let insert = leftNodes[openDepth] | ||
if (!insert) continue | ||
for (let i = 0; i < targetDepths.length; i++) { | ||
// Loop over possible expansion levels, starting with the | ||
// preferred one | ||
let targetDepth = targetDepths[(i + preferredTargetIndex) % targetDepths.length], expand = true | ||
if (targetDepth < 0) { expand = false; targetDepth = -targetDepth } | ||
let parent = $from.node(targetDepth - 1), index = $from.index(targetDepth - 1) | ||
if (parent.canReplaceWith(index, index, insert.type, insert.marks)) | ||
return this.replace($from.before(targetDepth), expand ? $to.after(targetDepth) : to, | ||
new Slice(closeFragment(slice.content, 0, slice.openStart, openDepth), | ||
openDepth, slice.openEnd)) | ||
} | ||
} | ||
return this.replace(from, to, slice) | ||
} | ||
function closeFragment(fragment, depth, oldOpen, newOpen, parent) { | ||
if (depth < oldOpen) { | ||
let first = fragment.firstChild | ||
fragment = fragment.replaceChild(0, first.copy(closeFragment(first.content, depth + 1, oldOpen, newOpen, first))) | ||
} | ||
if (depth > newOpen) | ||
fragment = parent.contentMatchAt(0).fillBefore(fragment).append(fragment) | ||
return fragment | ||
} | ||
// :: (number, number, Node) → this | ||
// Replace the given range with a node, but use `from` and `to` as | ||
// hints, rather than precise positions. When from and to are the same | ||
// and are at the start or end of a parent node in which the given | ||
// node doesn't fit, this method may _move_ them out towards a parent | ||
// that does allow the given node to be placed. When the given range | ||
// completely covers a parent node, this method may completely replace | ||
// that parent node. | ||
Transform.prototype.replaceRangeWith = function(from, to, node) { | ||
if (!node.isInline && from == to && this.doc.resolve(from).parent.content.size) { | ||
let point = insertPoint(this.doc, from, node.type) | ||
if (point != null) from = to = point | ||
} | ||
return this.replaceRange(from, to, new Slice(Fragment.from(node), 0, 0)) | ||
} | ||
// :: (number, number) → this | ||
// Delete the given range, expanding it to cover fully covered | ||
// parent nodes until a valid replace is found. | ||
Transform.prototype.deleteRange = function(from, to) { | ||
let $from = this.doc.resolve(from), $to = this.doc.resolve(to) | ||
let covered = coveredDepths($from, $to) | ||
for (let i = 0; i < covered.length; i++) { | ||
let depth = covered[i], last = i == covered.length - 1 | ||
if ((last && depth == 0) || $from.node(depth).type.contentMatch.validEnd) { | ||
from = $from.start(depth) | ||
to = $to.end(depth) | ||
break | ||
} | ||
if (depth > 0 && (last || $from.node(depth - 1).canReplace($from.index(depth - 1), $to.indexAfter(depth - 1)))) { | ||
from = $from.before(depth) | ||
to = $to.after(depth) | ||
break | ||
} | ||
} | ||
return this.delete(from, to) | ||
} | ||
// : (ResolvedPos, ResolvedPos) → [number] | ||
// Returns an array of all depths for which $from - $to spans the | ||
// whole content of the nodes at that depth. | ||
function coveredDepths($from, $to) { | ||
let result = [], minDepth = Math.min($from.depth, $to.depth) | ||
for (let d = minDepth; d >= 0; d--) { | ||
let start = $from.start(d) | ||
if (start < $from.pos - ($from.depth - d) || | ||
$to.end(d) > $to.pos + ($to.depth - d) || | ||
$from.node(d).type.spec.isolating || | ||
$to.node(d).type.spec.isolating) break | ||
if (start == $to.start(d)) result.push(d) | ||
} | ||
return result | ||
} |
@@ -1,4 +0,4 @@ | ||
const {ReplaceError} = require("prosemirror-model") | ||
import {ReplaceError} from "prosemirror-model" | ||
const {StepMap} = require("./map") | ||
import {StepMap} from "./map" | ||
@@ -9,5 +9,5 @@ function mustOverride() { throw new Error("Override me") } | ||
// ::- A step object wraps an atomic operation. It generally applies | ||
// ::- A step object represents an atomic change. It generally applies | ||
// only to the document it was created for, since the positions | ||
// associated with it will only make sense for that document. | ||
// stored in it will only make sense for that document. | ||
// | ||
@@ -19,3 +19,3 @@ // New steps are defined by creating classes that extend `Step`, | ||
// [`Step.jsonID`](#transform.Step^jsonID). | ||
class Step { | ||
export class Step { | ||
// :: (doc: Node) → StepResult | ||
@@ -29,4 +29,5 @@ // Applies this step to the given document, returning a result | ||
// :: () → StepMap | ||
// Get the step map that represents the changes made by this | ||
// step. | ||
// Get the step map that represents the changes made by this step, | ||
// and which can be used to transform between positions in the old | ||
// and the new document. | ||
getMap() { return StepMap.empty } | ||
@@ -51,23 +52,8 @@ | ||
// :: (n: number) → Step | ||
// Returns a copy of this step in which all positions have `n` added | ||
// to them. The main use for this is to take a step in one document, | ||
// and make it apply to a sub-document, or a larger document that | ||
// the original document is a part of. | ||
offset(_n) { return mustOverride() } | ||
// :: () → Object | ||
// Create a JSON-serializeable representation of this step. By | ||
// default, it'll create an object with the step's [JSON | ||
// id](#transform.Step^jsonID), and each of the steps's own properties, | ||
// automatically calling `toJSON` on the property values that have | ||
// such a method. | ||
toJSON() { | ||
let obj = {stepType: this.jsonID} | ||
for (let prop in this) if (this.hasOwnProperty(prop)) { | ||
let val = this[prop] | ||
obj[prop] = val && val.toJSON ? val.toJSON() : val | ||
} | ||
return obj | ||
} | ||
// Create a JSON-serializeable representation of this step. When | ||
// defining this for a custom subclass, make sure the result object | ||
// includes the step type's [JSON id](#transform.Step^jsonID) under | ||
// the `stepType` property. | ||
toJSON() { return mustOverride() } | ||
@@ -93,7 +79,6 @@ // :: (Schema, Object) → Step | ||
} | ||
exports.Step = Step | ||
// ::- The result of [applying](#transform.Step.apply) a step. Contains either a | ||
// new document or a failure value. | ||
class StepResult { | ||
export class StepResult { | ||
// : (?Node, ?string) | ||
@@ -128,2 +113,1 @@ constructor(doc, failed) { | ||
} | ||
exports.StepResult = StepResult |
@@ -1,5 +0,5 @@ | ||
const {Slice, Fragment} = require("prosemirror-model") | ||
import {Slice, Fragment} from "prosemirror-model" | ||
const {Transform} = require("./transform") | ||
const {ReplaceStep, ReplaceAroundStep} = require("./replace_step") | ||
import {Transform} from "./transform" | ||
import {ReplaceStep, ReplaceAroundStep} from "./replace_step" | ||
@@ -15,3 +15,3 @@ function canCut(node, start, end) { | ||
// [isolating](#model.NodeSpec.isolating) parent nodes. | ||
function liftTarget(range) { | ||
export function liftTarget(range) { | ||
let parent = range.parent | ||
@@ -27,3 +27,2 @@ let content = parent.content.cutByIndex(range.startIndex, range.endIndex) | ||
} | ||
exports.liftTarget = liftTarget | ||
@@ -34,4 +33,4 @@ // :: (NodeRange, number) → this | ||
// the depth specified by `target`. You'll probably want to use | ||
// `liftTarget` to compute `target`, in order to be sure the lift is | ||
// valid. | ||
// [`liftTarget`](#transform.liftTarget) to compute `target`, to make | ||
// sure the lift is valid. | ||
Transform.prototype.lift = function(range, target) { | ||
@@ -72,31 +71,29 @@ let {$from, $to, depth} = range | ||
// could be found. | ||
function findWrapping(range, nodeType, attrs, innerRange = range) { | ||
let wrap = {type: nodeType, attrs} | ||
let around = findWrappingOutside(range, wrap) | ||
let inner = around && findWrappingInside(innerRange, wrap) | ||
export function findWrapping(range, nodeType, attrs, innerRange = range) { | ||
let around = findWrappingOutside(range, nodeType) | ||
let inner = around && findWrappingInside(innerRange, nodeType) | ||
if (!inner) return null | ||
return around.concat(wrap).concat(inner) | ||
return around.map(withAttrs).concat({type: nodeType, attrs}).concat(inner.map(withAttrs)) | ||
} | ||
exports.findWrapping = findWrapping | ||
function findWrappingOutside(range, wrap) { | ||
function withAttrs(type) { return {type, attrs: null} } | ||
function findWrappingOutside(range, type) { | ||
let {parent, startIndex, endIndex} = range | ||
let around = parent.contentMatchAt(startIndex).findWrapping(wrap.type, wrap.attrs) | ||
let around = parent.contentMatchAt(startIndex).findWrapping(type) | ||
if (!around) return null | ||
let outer = around.length ? around[0] : wrap | ||
if (!parent.canReplaceWith(startIndex, endIndex, outer.type, outer.attrs)) | ||
return null | ||
return around | ||
let outer = around.length ? around[0] : type | ||
return parent.canReplaceWith(startIndex, endIndex, outer) ? around : null | ||
} | ||
function findWrappingInside(range, wrap) { | ||
function findWrappingInside(range, type) { | ||
let {parent, startIndex, endIndex} = range | ||
let inner = parent.child(startIndex) | ||
let inside = wrap.type.contentExpr.start(wrap.attrs).findWrappingFor(inner) | ||
let inside = type.contentMatch.findWrapping(inner.type) | ||
if (!inside) return null | ||
let last = inside.length ? inside[inside.length - 1] : wrap | ||
let innerMatch = last.type.contentExpr.start(last.attrs) | ||
for (let i = startIndex; i < endIndex; i++) | ||
innerMatch = innerMatch && innerMatch.matchNode(parent.child(i)) | ||
if (!innerMatch || !innerMatch.validEnd()) return null | ||
let lastType = inside.length ? inside[inside.length - 1] : type | ||
let innerMatch = lastType.contentMatch | ||
for (let i = startIndex; innerMatch && i < endIndex; i++) | ||
innerMatch = innerMatch.matchType(parent.child(i).type) | ||
if (!innerMatch || !innerMatch.validEnd) return null | ||
return inside | ||
@@ -108,3 +105,3 @@ } | ||
// The wrappers are assumed to be valid in this position, and should | ||
// probably be computed with `findWrapping`. | ||
// probably be computed with [`findWrapping`](#transform.findWrapping). | ||
Transform.prototype.wrap = function(range, wrappers) { | ||
@@ -128,3 +125,3 @@ let content = Fragment.empty | ||
// Ensure all markup that isn't allowed in the new node type is cleared | ||
this.clearNonMatching(this.mapping.slice(mapFrom).map(pos, 1), type.contentExpr.start(attrs)) | ||
this.clearIncompatible(this.mapping.slice(mapFrom).map(pos, 1), type) | ||
let mapping = this.mapping.slice(mapFrom) | ||
@@ -141,3 +138,4 @@ let startM = mapping.map(pos, 1), endM = mapping.map(pos + node.nodeSize, 1) | ||
// :: (number, ?NodeType, ?Object, ?[Mark]) → this | ||
// Change the type and attributes of the node after `pos`. | ||
// Change the type, attributes, and/or marks of the node at `pos`. | ||
// When `nodeType` is null, the existing node type is preserved, | ||
Transform.prototype.setNodeType = function(pos, type, attrs, marks) { | ||
@@ -151,3 +149,3 @@ let node = this.doc.nodeAt(pos) | ||
if (!type.validContent(node.content, attrs)) | ||
if (!type.validContent(node.content)) | ||
throw new RangeError("Invalid content for node type " + type.name) | ||
@@ -161,3 +159,3 @@ | ||
// Check whether splitting at the given position is allowed. | ||
function canSplit(doc, pos, depth = 1, typesAfter) { | ||
export function canSplit(doc, pos, depth = 1, typesAfter) { | ||
let $pos = doc.resolve(pos), base = $pos.depth - depth | ||
@@ -167,3 +165,3 @@ let innerType = (typesAfter && typesAfter[typesAfter.length - 1]) || $pos.parent | ||
!$pos.parent.canReplace($pos.index(), $pos.parent.childCount) || | ||
!innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount), innerType.attrs)) | ||
!innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount))) | ||
return false | ||
@@ -175,3 +173,3 @@ for (let d = $pos.depth - 1, i = depth - 2; d > base; d--, i--) { | ||
if (after != node) rest = rest.replaceChild(0, after.type.create(after.attrs)) | ||
if (!node.canReplace(index + 1, node.childCount) || !after.type.validContent(rest, after.attrs)) | ||
if (!node.canReplace(index + 1, node.childCount) || !after.type.validContent(rest)) | ||
return false | ||
@@ -181,6 +179,4 @@ } | ||
let baseType = typesAfter && typesAfter[0] | ||
return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type, | ||
baseType ? baseType.attrs : $pos.node(base + 1).attrs) | ||
return $pos.node(base).canReplaceWith(index, index, baseType ? baseType.type : $pos.node(base + 1).type) | ||
} | ||
exports.canSplit = canSplit | ||
@@ -206,3 +202,3 @@ // :: (number, ?number, ?[?{type: NodeType, attrs: ?Object}]) → this | ||
// joined. | ||
function canJoin(doc, pos) { | ||
export function canJoin(doc, pos) { | ||
let $pos = doc.resolve(pos), index = $pos.index() | ||
@@ -212,3 +208,2 @@ return joinable($pos.nodeBefore, $pos.nodeAfter) && | ||
} | ||
exports.canJoin = canJoin | ||
@@ -223,3 +218,3 @@ function joinable(a, b) { | ||
// point, if any. | ||
function joinPoint(doc, pos, dir = -1) { | ||
export function joinPoint(doc, pos, dir = -1) { | ||
let $pos = doc.resolve(pos) | ||
@@ -243,3 +238,2 @@ for (let d = $pos.depth;; d--) { | ||
} | ||
exports.joinPoint = joinPoint | ||
@@ -254,3 +248,3 @@ // :: (number, ?number, ?bool) → this | ||
// :: (Node, number, NodeType, ?Object) → ?number | ||
// :: (Node, number, NodeType) → ?number | ||
// Try to find a point where a node of the given type can be inserted | ||
@@ -260,5 +254,5 @@ // near `pos`, by searching up the node hierarchy when `pos` itself | ||
// null if no position was found. | ||
function insertPoint(doc, pos, nodeType, attrs) { | ||
export function insertPoint(doc, pos, nodeType) { | ||
let $pos = doc.resolve(pos) | ||
if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType, attrs)) return pos | ||
if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType)) return pos | ||
@@ -268,3 +262,3 @@ if ($pos.parentOffset == 0) | ||
let index = $pos.index(d) | ||
if ($pos.node(d).canReplaceWith(index, index, nodeType, attrs)) return $pos.before(d + 1) | ||
if ($pos.node(d).canReplaceWith(index, index, nodeType)) return $pos.before(d + 1) | ||
if (index > 0) return null | ||
@@ -275,6 +269,5 @@ } | ||
let index = $pos.indexAfter(d) | ||
if ($pos.node(d).canReplaceWith(index, index, nodeType, attrs)) return $pos.after(d + 1) | ||
if ($pos.node(d).canReplaceWith(index, index, nodeType)) return $pos.after(d + 1) | ||
if (index < $pos.node(d).childCount) return null | ||
} | ||
} | ||
exports.insertPoint = insertPoint |
@@ -1,17 +0,16 @@ | ||
const {Mapping} = require("./map") | ||
import {Mapping} from "./map" | ||
class TransformError extends Error { | ||
export class TransformError extends Error { | ||
constructor(message) { super(message) } | ||
get name() { return "TransformError" } | ||
} | ||
exports.TransformError = TransformError | ||
// ::- Abstraction to build up and track such an array of | ||
// [steps](#transform.Step). | ||
// ::- Abstraction to build up and track an array of | ||
// [steps](#transform.Step) representing a document transformation. | ||
// | ||
// The high-level transforming methods return the `Transform` object | ||
// itself, so that they can be chained. | ||
class Transform { | ||
// Most transforming methods return the `Transform` object itself, so | ||
// that they can be chained. | ||
export class Transform { | ||
// :: (Node) | ||
// Create a transformation that starts with the given document. | ||
// Create a transform that starts with the given document. | ||
constructor(doc) { | ||
@@ -33,8 +32,8 @@ // :: Node | ||
// :: Node The document at the start of the transformation. | ||
// :: Node The starting document. | ||
get before() { return this.docs.length ? this.docs[0] : this.doc } | ||
// :: (step: Step) → this | ||
// Apply a new step in this transformation, saving the result. | ||
// Throws an error when the step fails. | ||
// Apply a new step in this transform, saving the result. Throws an | ||
// error when the step fails. | ||
step(object) { | ||
@@ -56,3 +55,4 @@ let result = this.maybeStep(object) | ||
// :: bool | ||
// True when this transaction changes the document. | ||
// True when the document has been changed (when there are any | ||
// steps). | ||
get docChanged() { | ||
@@ -69,2 +69,1 @@ return this.steps.length > 0 | ||
} | ||
exports.Transform = Transform |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
320887
26
4292
24
+ Addedprosemirror-model@0.23.1(transitive)
- Removedprosemirror-model@0.22.0(transitive)
Updatedprosemirror-model@^0.23.0