Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kurtharriger/8ee9193e8e39f1f607e8cbb13f2c843d to your computer and use it in GitHub Desktop.
Save kurtharriger/8ee9193e8e39f1f607e8cbb13f2c843d to your computer and use it in GitHub Desktop.
prosemirror.state
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
// ::- Persistent data structure representing an ordered mapping from
// strings to values, with some convenient update methods.
function OrderedMap(content) {
this.content = content;
}
OrderedMap.prototype = {
constructor: OrderedMap,
find: function (key) {
for (var i = 0; i < this.content.length; i += 2) if (this.content[i] === key) return i;
return -1;
},
// :: (string) → ?any
// Retrieve the value stored under `key`, or return undefined when
// no such key exists.
get: function (key) {
var found = this.find(key);
return found == -1 ? undefined : this.content[found + 1];
},
// :: (string, any, ?string) → OrderedMap
// Create a new map by replacing the value of `key` with a new
// value, or adding a binding to the end of the map. If `newKey` is
// given, the key of the binding will be replaced with that key.
update: function (key, value, newKey) {
var self = newKey && newKey != key ? this.remove(newKey) : this;
var found = self.find(key),
content = self.content.slice();
if (found == -1) {
content.push(newKey || key, value);
} else {
content[found + 1] = value;
if (newKey) content[found] = newKey;
}
return new OrderedMap(content);
},
// :: (string) → OrderedMap
// Return a map with the given key removed, if it existed.
remove: function (key) {
var found = this.find(key);
if (found == -1) return this;
var content = this.content.slice();
content.splice(found, 2);
return new OrderedMap(content);
},
// :: (string, any) → OrderedMap
// Add a new key to the start of the map.
addToStart: function (key, value) {
return new OrderedMap([key, value].concat(this.remove(key).content));
},
// :: (string, any) → OrderedMap
// Add a new key to the end of the map.
addToEnd: function (key, value) {
var content = this.remove(key).content.slice();
content.push(key, value);
return new OrderedMap(content);
},
// :: (string, string, any) → OrderedMap
// Add a key after the given key. If `place` is not found, the new
// key is added to the end.
addBefore: function (place, key, value) {
var without = this.remove(key),
content = without.content.slice();
var found = without.find(place);
content.splice(found == -1 ? content.length : found, 0, key, value);
return new OrderedMap(content);
},
// :: ((key: string, value: any))
// Call the given function for each key/value pair in the map, in
// order.
forEach: function (f) {
for (var i = 0; i < this.content.length; i += 2) f(this.content[i], this.content[i + 1]);
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a new map by prepending the keys in this map that don't
// appear in `map` before the keys in `map`.
prepend: function (map) {
map = OrderedMap.from(map);
if (!map.size) return this;
return new OrderedMap(map.content.concat(this.subtract(map).content));
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a new map by appending the keys in this map that don't
// appear in `map` after the keys in `map`.
append: function (map) {
map = OrderedMap.from(map);
if (!map.size) return this;
return new OrderedMap(this.subtract(map).content.concat(map.content));
},
// :: (union<Object, OrderedMap>) → OrderedMap
// Create a map containing all the keys in this map that don't
// appear in `map`.
subtract: function (map) {
var result = this;
map = OrderedMap.from(map);
for (var i = 0; i < map.content.length; i += 2) result = result.remove(map.content[i]);
return result;
},
// :: number
// The amount of keys in this map.
get size() {
return this.content.length >> 1;
}
};
// :: (?union<Object, OrderedMap>) → OrderedMap
// Return a map with the given content. If null, create an empty
// map. If given an ordered map, return that map itself. If given an
// object, create a map from the object's properties.
OrderedMap.from = function (value) {
if (value instanceof OrderedMap) return value;
var content = [];
if (value) for (var prop in value) content.push(prop, value[prop]);
return new OrderedMap(content);
};
module.exports = OrderedMap;
},{}],2:[function(require,module,exports){
function compareDeep(a, b) {
if (a === b) {
return true;
}
if (!(a && typeof a == "object") || !(b && typeof b == "object")) {
return false;
}
var array = Array.isArray(a);
if (Array.isArray(b) != array) {
return false;
}
if (array) {
if (a.length != b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!compareDeep(a[i], b[i])) {
return false;
}
}
} else {
for (var p in a) {
if (!(p in b) || !compareDeep(a[p], b[p])) {
return false;
}
}
for (var p$1 in b) {
if (!(p$1 in a)) {
return false;
}
}
}
return true;
}
exports.compareDeep = compareDeep;
},{}],3:[function(require,module,exports){
var ref = require("./fragment");
var Fragment = ref.Fragment;
var ref$1 = require("./mark");
var Mark = ref$1.Mark;
var ContentExpr = function (nodeType, elements, inlineContent) {
this.nodeType = nodeType;
this.elements = elements;
this.inlineContent = inlineContent;
};
var prototypeAccessors = { isLeaf: {} };
prototypeAccessors.isLeaf.get = function () {
return this.elements.length == 0;
};
// : (?Object) → ContentMatch
// The content match at the start of this expression.
ContentExpr.prototype.start = function (attrs) {
return new ContentMatch(this, attrs, 0, 0);
};
// : (NodeType, ?Object, ?Object) → ?ContentMatch
// Try to find a match that matches the given node, anywhere in the
// expression. (Useful when synthesizing a match for a node that's
// open to the left.)
ContentExpr.prototype.atType = function (parentAttrs, type, attrs, marks) {
var this$1 = this;
if (marks === void 0) marks = Mark.none;
for (var i = 0; i < this.elements.length; i++) {
if (this$1.elements[i].matchesType(type, attrs, marks, parentAttrs, this$1)) {
return new ContentMatch(this$1, parentAttrs, i, 0);
}
}
};
ContentExpr.prototype.matches = function (attrs, fragment, from, to) {
return this.start(attrs).matchToEnd(fragment, from, to);
};
// Get a position in a known-valid fragment. If this is a simple
// (single-element) expression, we don't have to do any matching,
// and can simply skip to the position with count `index`.
ContentExpr.prototype.getMatchAt = function (attrs, fragment, index) {
if (index === void 0) index = fragment.childCount;
if (this.elements.length == 1) {
return new ContentMatch(this, attrs, 0, index);
} else {
return this.start(attrs).matchFragment(fragment, 0, index);
}
};
ContentExpr.prototype.checkReplace = function (attrs, content, from, to, replacement, start, end) {
var this$1 = this;
if (replacement === void 0) replacement = Fragment.empty;
if (start === void 0) start = 0;
if (end === void 0) end = replacement.childCount;
// Check for simple case, where the expression only has a single element
// (Optimization to avoid matching more than we need)
if (this.elements.length == 1) {
var elt = this.elements[0];
if (!checkCount(elt, content.childCount - (to - from) + (end - start), attrs, this)) {
return false;
}
for (var i = start; i < end; i++) {
if (!elt.matches(replacement.child(i), attrs, this$1)) {
return false;
}
}
return true;
}
var match = this.getMatchAt(attrs, content, from).matchFragment(replacement, start, end);
return match ? match.matchToEnd(content, to) : false;
};
ContentExpr.prototype.checkReplaceWith = function (attrs, content, from, to, type, typeAttrs, marks) {
if (this.elements.length == 1) {
var elt = this.elements[0];
if (!checkCount(elt, content.childCount - (to - from) + 1, attrs, this)) {
return false;
}
return elt.matchesType(type, typeAttrs, marks, attrs, this);
}
var match = this.getMatchAt(attrs, content, from).matchType(type, typeAttrs, marks);
return match ? match.matchToEnd(content, to) : false;
};
ContentExpr.prototype.compatible = function (other) {
var this$1 = this;
for (var i = 0; i < this.elements.length; i++) {
var elt = this$1.elements[i];
for (var j = 0; j < other.elements.length; j++) {
if (other.elements[j].compatible(elt)) {
return true;
}
}
}
return false;
};
ContentExpr.prototype.generateContent = function (attrs) {
return this.start(attrs).fillBefore(Fragment.empty, true);
};
ContentExpr.parse = function (nodeType, expr) {
var this$1 = this;
var elements = [],
pos = 0,
inline = null;
for (;;) {
pos += /^\s*/.exec(expr.slice(pos))[0].length;
if (pos == expr.length) {
break;
}
var types = /^(?:(\w+)|\(\s*(\w+(?:\s*\|\s*\w+)*)\s*\))/.exec(expr.slice(pos));
if (!types) {
throw new SyntaxError("Invalid content expression '" + expr + "' at " + pos);
}
pos += types[0].length;
var attrs = /^\[([^\]]+)\]/.exec(expr.slice(pos));
if (attrs) {
pos += attrs[0].length;
}
var marks = /^<(?:(_)|\s*(\w+(?:\s+\w+)*)\s*)>/.exec(expr.slice(pos));
if (marks) {
pos += marks[0].length;
}
var repeat = /^(?:([+*?])|\{\s*(\d+|\.\w+)\s*(,\s*(\d+|\.\w+)?)?\s*\})/.exec(expr.slice(pos));
if (repeat) {
pos += repeat[0].length;
}
var nodeTypes = expandTypes(nodeType.schema, types[1] ? [types[1]] : types[2].split(/\s*\|\s*/));
for (var i = 0; i < nodeTypes.length; i++) {
if (inline == null) {
inline = nodeTypes[i].isInline;
} else if (inline != nodeTypes[i].isInline) {
throw new SyntaxError("Mixing inline and block content in a single node");
}
}
var attrSet = !attrs ? null : parseAttrs(nodeType, attrs[1]);
var markSet = !marks ? false : marks[1] ? true : this$1.gatherMarks(nodeType.schema, marks[2].split(/\s+/));
var ref = parseRepeat(nodeType, repeat);
var min = ref.min;
var max = ref.max;
if (min != 0 && (nodeTypes[0].hasRequiredAttrs(attrSet) || nodeTypes[0].isText)) {
throw new SyntaxError("Node type " + types[0] + " in type " + nodeType.name + " is required, but has non-optional attributes");
}
var newElt = new ContentElement(nodeTypes, attrSet, markSet, min, max);
for (var i$1 = elements.length - 1; i$1 >= 0; i$1--) {
var prev = elements[i$1];
if (prev.min != prev.max && prev.overlaps(newElt)) {
throw new SyntaxError("Possibly ambiguous overlapping adjacent content expressions in '" + expr + "'");
}
if (prev.min != 0) {
break;
}
}
elements.push(newElt);
}
return new ContentExpr(nodeType, elements, !!inline);
};
ContentExpr.gatherMarks = function (schema, marks) {
var found = [];
for (var i = 0; i < marks.length; i++) {
var name = marks[i],
mark = schema.marks[name],
ok = mark;
if (mark) {
found.push(mark);
} else {
for (var prop in schema.marks) {
var mark$1 = schema.marks[prop];
if (name == "_" || mark$1.spec.group && mark$1.spec.group.split(" ").indexOf(name) > -1) {
found.push(ok = mark$1);
}
}
}
if (!ok) {
throw new SyntaxError("Unknown mark type: '" + marks[i] + "'");
}
}
return found;
};
Object.defineProperties(ContentExpr.prototype, prototypeAccessors);
exports.ContentExpr = ContentExpr;
var ContentElement = function (nodeTypes, attrs, marks, min, max) {
this.nodeTypes = nodeTypes;
this.attrs = attrs;
this.marks = marks;
this.min = min;
this.max = max;
};
ContentElement.prototype.matchesType = function (type, attrs, marks, parentAttrs, parentExpr) {
var this$1 = this;
if (this.nodeTypes.indexOf(type) == -1) {
return false;
}
if (this.attrs) {
if (!attrs) {
return false;
}
for (var prop in this$1.attrs) {
if (attrs[prop] != resolveValue(this$1.attrs[prop], parentAttrs, parentExpr)) {
return false;
}
}
}
if (this.marks === true) {
return true;
}
if (this.marks === false) {
return marks.length == 0;
}
for (var i = 0; i < marks.length; i++) {
if (this$1.marks.indexOf(marks[i].type) == -1) {
return false;
}
}
return true;
};
ContentElement.prototype.matches = function (node, parentAttrs, parentExpr) {
return this.matchesType(node.type, node.attrs, node.marks, parentAttrs, parentExpr);
};
ContentElement.prototype.compatible = function (other) {
var this$1 = this;
for (var i = 0; i < this.nodeTypes.length; i++) {
if (other.nodeTypes.indexOf(this$1.nodeTypes[i]) != -1) {
return true;
}
}
return false;
};
ContentElement.prototype.constrainedAttrs = function (parentAttrs, expr) {
var this$1 = this;
if (!this.attrs) {
return null;
}
var attrs = Object.create(null);
for (var prop in this$1.attrs) {
attrs[prop] = resolveValue(this$1.attrs[prop], parentAttrs, expr);
}
return attrs;
};
ContentElement.prototype.createFiller = function (parentAttrs, expr) {
var type = this.nodeTypes[0],
attrs = type.computeAttrs(this.constrainedAttrs(parentAttrs, expr));
return type.create(attrs, type.contentExpr.generateContent(attrs));
};
ContentElement.prototype.defaultType = function () {
var first = this.nodeTypes[0];
if (!(first.hasRequiredAttrs() || first.isText)) {
return first;
}
};
ContentElement.prototype.overlaps = function (other) {
return this.nodeTypes.some(function (t) {
return other.nodeTypes.indexOf(t) > -1;
});
};
ContentElement.prototype.allowsMark = function (markType) {
return this.marks === true || this.marks && this.marks.indexOf(markType) > -1;
};
// ::- Represents a partial match of a node type's [content
// expression](#model.NodeSpec), and can be used to find out whether further
// content matches here, and whether a given position is a valid end
// of the parent node.
var ContentMatch = function (expr, attrs, index, count) {
this.expr = expr;
this.attrs = attrs;
this.index = index;
this.count = count;
};
var prototypeAccessors$1 = { element: {}, nextElement: {} };
prototypeAccessors$1.element.get = function () {
return this.expr.elements[this.index];
};
prototypeAccessors$1.nextElement.get = function () {
var this$1 = this;
for (var i = this.index, count = this.count; i < this.expr.elements.length; i++) {
var element = this$1.expr.elements[i];
if (this$1.resolveValue(element.max) > count) {
return element;
}
count = 0;
}
};
ContentMatch.prototype.move = function (index, count) {
return new ContentMatch(this.expr, this.attrs, index, count);
};
ContentMatch.prototype.resolveValue = function (value) {
return value instanceof AttrValue ? resolveValue(value, this.attrs, this.expr) : value;
};
// :: (Node) → ?ContentMatch
// Match a node, returning a new match after the node if successful.
ContentMatch.prototype.matchNode = function (node) {
return this.matchType(node.type, node.attrs, node.marks);
};
// :: (NodeType, ?Object, [Mark]) → ?ContentMatch
// Match a node type and marks, returning an match after that node
// if successful.
ContentMatch.prototype.matchType = function (type, attrs, marks) {
var this$1 = this;
if (marks === void 0) marks = Mark.none;
for (var ref = this, index = ref.index, count = ref.count; index < this.expr.elements.length; index++, count = 0) {
var elt = this$1.expr.elements[index],
max = this$1.resolveValue(elt.max);
if (count < max && elt.matchesType(type, attrs, marks, this$1.attrs, this$1.expr)) {
count++;
return this$1.move(index, count);
}
if (count < this$1.resolveValue(elt.min)) {
return null;
}
}
};
// :: (Fragment, ?number, ?number) → ?union<ContentMatch, bool>
// Try to match a fragment. Returns a new match when successful,
// `null` when it ran into a required element it couldn't fit, and
// `false` if it reached the end of the expression without
// matching all nodes.
ContentMatch.prototype.matchFragment = function (fragment, from, to) {
var this$1 = this;
if (from === void 0) from = 0;
if (to === void 0) to = fragment.childCount;
if (from == to) {
return this;
}
var fragPos = from,
end = this.expr.elements.length;
for (var ref = this, index = ref.index, count = ref.count; index < end; index++, count = 0) {
var elt = this$1.expr.elements[index],
max = this$1.resolveValue(elt.max);
while (count < max && fragPos < to) {
if (elt.matches(fragment.child(fragPos), this$1.attrs, this$1.expr)) {
count++;
if (++fragPos == to) {
return this$1.move(index, count);
}
} else {
break;
}
}
if (count < this$1.resolveValue(elt.min)) {
return null;
}
}
return false;
};
// :: (Fragment, ?number, ?number) → bool
// Returns true only if the fragment matches here, and reaches all
// the way to the end of the content expression.
ContentMatch.prototype.matchToEnd = function (fragment, start, end) {
var matched = this.matchFragment(fragment, start, end);
return matched && matched.validEnd() || false;
};
// :: () → bool
// Returns true if this position represents a valid end of the
// expression (no required content follows after it).
ContentMatch.prototype.validEnd = function () {
var this$1 = this;
for (var i = this.index, count = this.count; i < this.expr.elements.length; i++, count = 0) {
if (count < this$1.resolveValue(this$1.expr.elements[i].min)) {
return false;
}
}
return true;
};
// :: (Fragment, bool, ?number) → ?Fragment
// 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.
ContentMatch.prototype.fillBefore = function (after, toEnd, startIndex) {
var this$1 = this;
var added = [],
match = this,
index = startIndex || 0,
end = this.expr.elements.length;
for (;;) {
var fits = match.matchFragment(after, index);
if (fits && (!toEnd || fits.validEnd())) {
return Fragment.from(added);
}
if (fits === false) {
return null;
} // Matched to end with content remaining
var elt = match.element;
if (match.count < this$1.resolveValue(elt.min)) {
added.push(elt.createFiller(this$1.attrs, this$1.expr));
match = match.move(match.index, match.count + 1);
} else if (match.index < end) {
match = match.move(match.index + 1, 0);
} else if (after.childCount > index) {
return null;
} else {
return Fragment.from(added);
}
}
};
ContentMatch.prototype.possibleContent = function () {
var this$1 = this;
var found = [];
for (var i = this.index, count = this.count; i < this.expr.elements.length; i++, count = 0) {
var elt = this$1.expr.elements[i],
attrs = elt.constrainedAttrs(this$1.attrs, this$1.expr);
if (count < this$1.resolveValue(elt.max)) {
for (var j = 0; j < elt.nodeTypes.length; j++) {
var type = elt.nodeTypes[j];
if (!type.hasRequiredAttrs(attrs) && !type.isText) {
found.push({ type: type, attrs: attrs });
}
}
}
if (this$1.resolveValue(elt.min) > count) {
break;
}
}
return found;
};
// :: (MarkType) → bool
// Check whether a node with the given mark type is allowed after
// this position.
ContentMatch.prototype.allowsMark = function (markType) {
return this.element.allowsMark(markType);
};
// :: (NodeType, ?Object, ?[Mark]) → ?[{type: NodeType, attrs: Object}]
// Find a set of wrapping node types that would allow a node of type
// `target` with attributes `targetAttrs` to appear at this
// position. The result may be empty (when it fits directly) and
// will be null when no such wrapping exists.
ContentMatch.prototype.findWrapping = function (target, targetAttrs, targetMarks) {
var seen = Object.create(null),
first = { match: this, via: null },
active = [first];
while (active.length) {
var current = active.shift(),
match = current.match;
if (match.matchType(target, targetAttrs, targetMarks)) {
var result = [];
for (var obj = current; obj != first; obj = obj.via) {
result.push({ type: obj.match.expr.nodeType, attrs: obj.match.attrs });
}
return result.reverse();
}
var possible = match.possibleContent();
for (var i = 0; i < possible.length; i++) {
var ref = possible[i];
var type = ref.type;
var attrs = ref.attrs;
var fullAttrs = type.computeAttrs(attrs);
if (!type.isLeaf && !(type.name in seen) && (current == first || match.matchType(type, fullAttrs).validEnd())) {
active.push({ match: type.contentExpr.start(fullAttrs), via: current });
seen[type.name] = true;
}
}
}
};
// :: (Node) → ?[{type: NodeType, attrs: Object}]
// Call [`findWrapping`](#model.ContentMatch.findWrapping) with the
// properties of the given node.
ContentMatch.prototype.findWrappingFor = function (node) {
return this.findWrapping(node.type, node.attrs, node.marks);
};
Object.defineProperties(ContentMatch.prototype, prototypeAccessors$1);
exports.ContentMatch = ContentMatch;
var AttrValue = function (attr) {
this.attr = attr;
};
function parseValue(nodeType, value) {
if (value.charAt(0) == ".") {
var attr = value.slice(1);
if (!nodeType.attrs[attr]) {
throw new SyntaxError("Node type " + nodeType.name + " has no attribute " + attr);
}
return new AttrValue(attr);
} else {
return JSON.parse(value);
}
}
function resolveValue(value, attrs, expr) {
if (!(value instanceof AttrValue)) {
return value;
}
var attrVal = attrs && attrs[value.attr];
return attrVal !== undefined ? attrVal : expr.nodeType.defaultAttrs[value.attr];
}
function checkCount(elt, count, attrs, expr) {
return count >= resolveValue(elt.min, attrs, expr) && count <= resolveValue(elt.max, attrs, expr);
}
function expandTypes(schema, types) {
var result = [];
types.forEach(function (type) {
var found = schema.nodes[type];
if (found) {
if (result.indexOf(found) == -1) {
result.push(found);
}
} else {
for (var name in schema.nodes) {
var nodeType = schema.nodes[name];
if (nodeType.groups.indexOf(type) > -1 && result.indexOf(nodeType) == -1) {
found = result.push(nodeType);
}
}
}
if (!found) {
throw new SyntaxError("Node type or group '" + type + "' does not exist");
}
});
return result;
}
var many = 2e9; // Big number representable as a 32-bit int
function parseRepeat(nodeType, match) {
var min = 1,
max = 1;
if (match) {
if (match[1] == "+") {
max = many;
} else if (match[1] == "*") {
min = 0;
max = many;
} else if (match[1] == "?") {
min = 0;
} else if (match[2]) {
min = parseValue(nodeType, match[2]);
if (match[3]) {
max = match[4] ? parseValue(nodeType, match[4]) : many;
} else {
max = min;
}
}
if (max == 0 || min > max) {
throw new SyntaxError("Invalid repeat count in '" + match[0] + "'");
}
}
return { min: min, max: max };
}
function parseAttrs(nodeType, expr) {
var parts = expr.split(/\s*,\s*/);
var attrs = Object.create(null);
for (var i = 0; i < parts.length; i++) {
var match = /^(\w+)=(\w+|\"(?:\\.|[^\\])*\"|\.\w+)$/.exec(parts[i]);
if (!match) {
throw new SyntaxError("Invalid attribute syntax: " + parts[i]);
}
attrs[match[1]] = parseValue(nodeType, match[2]);
}
return attrs;
}
},{"./fragment":5,"./mark":8}],4:[function(require,module,exports){
function findDiffStart(a, b, pos) {
for (var i = 0;; i++) {
if (i == a.childCount || i == b.childCount) {
return a.childCount == b.childCount ? null : pos;
}
var 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) {
for (var j = 0; childA.text[j] == childB.text[j]; j++) {
pos++;
}
return pos;
}
if (childA.content.size || childB.content.size) {
var inner = findDiffStart(childA.content, childB.content, pos + 1);
if (inner != null) {
return inner;
}
}
pos += childA.nodeSize;
}
}
exports.findDiffStart = findDiffStart;
function findDiffEnd(a, b, posA, posB) {
for (var iA = a.childCount, iB = b.childCount;;) {
if (iA == 0 || iB == 0) {
return iA == iB ? null : { a: posA, b: posB };
}
var 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) {
var same = 0,
minSize = Math.min(childA.text.length, childB.text.length);
while (same < minSize && childA.text[childA.text.length - same - 1] == childB.text[childB.text.length - same - 1]) {
same++;posA--;posB--;
}
return { a: posA, b: posB };
}
if (childA.content.size || childB.content.size) {
var inner = findDiffEnd(childA.content, childB.content, posA - 1, posB - 1);
if (inner) {
return inner;
}
}
posA -= size;posB -= size;
}
}
exports.findDiffEnd = findDiffEnd;
},{}],5:[function(require,module,exports){
var ref = require("./diff");
var findDiffStart = ref.findDiffStart;
var findDiffEnd = ref.findDiffEnd;
// ::- Fragment is the type used to represent a node's collection of
// child nodes.
//
// Fragments are persistent data structures. That means you should
// _not_ mutate them or their content, but create new instances
// whenever needed. The API tries to make this easy.
var Fragment = function (content, size) {
var this$1 = this;
this.content = content;
this.size = size || 0;
if (size == null) {
for (var i = 0; i < content.length; i++) {
this$1.size += content[i].nodeSize;
}
}
};
var prototypeAccessors = { firstChild: {}, lastChild: {}, childCount: {} };
// :: (number, number, (node: Node, start: number, parent: Node, index: number) → ?bool)
// 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`.
Fragment.prototype.nodesBetween = function (from, to, f, nodeStart, parent) {
var this$1 = this;
if (nodeStart === void 0) nodeStart = 0;
for (var i = 0, pos = 0; pos < to; i++) {
var child = this$1.content[i],
end = pos + child.nodeSize;
if (end > from && f(child, nodeStart + pos, parent, i) !== false && child.content.size) {
var start = pos + 1;
child.nodesBetween(Math.max(0, from - start), Math.min(child.content.size, to - start), f, nodeStart + start);
}
pos = end;
}
};
// :: ((node: Node, pos: number, parent: Node) → ?bool)
// Call the given callback for every descendant node. The callback
// may return `false` to prevent traversal of its child nodes.
Fragment.prototype.descendants = function (f) {
this.nodesBetween(0, this.size, f);
};
// : (number, number, ?string, ?string) → string
Fragment.prototype.textBetween = function (from, to, blockSeparator, leafText) {
var text = "",
separated = true;
this.nodesBetween(from, to, function (node, pos) {
if (node.isText) {
text += node.text.slice(Math.max(from, pos) - pos, to - pos);
separated = !blockSeparator;
} else if (node.isLeaf && leafText) {
text += leafText;
separated = !blockSeparator;
} else if (!separated && node.isBlock) {
text += blockSeparator;
separated = true;
}
}, 0);
return text;
};
// :: (Fragment) → Fragment
// Create a new fragment containing the content of this fragment and
// `other`.
Fragment.prototype.append = function (other) {
if (!other.size) {
return this;
}
if (!this.size) {
return other;
}
var last = this.lastChild,
first = other.firstChild,
content = this.content.slice(),
i = 0;
if (last.isText && last.sameMarkup(first)) {
content[content.length - 1] = last.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);
};
// :: (number, ?number) → Fragment
// Cut out the sub-fragment between the two given positions.
Fragment.prototype.cut = function (from, to) {
var this$1 = this;
if (to == null) {
to = this.size;
}
if (from == 0 && to == this.size) {
return this;
}
var result = [],
size = 0;
if (to > from) {
for (var i = 0, pos = 0; pos < to; i++) {
var child = this$1.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);
};
Fragment.prototype.cutByIndex = function (from, to) {
if (from == to) {
return Fragment.empty;
}
if (from == 0 && to == this.content.length) {
return this;
}
return new Fragment(this.content.slice(from, to));
};
// :: (number, Node) → Fragment
// Create a new fragment in which the node at the given index is
// replaced by the given node.
Fragment.prototype.replaceChild = function (index, node) {
var current = this.content[index];
if (current == node) {
return this;
}
var copy = this.content.slice();
var size = this.size + node.nodeSize - current.nodeSize;
copy[index] = node;
return new Fragment(copy, size);
};
// : (Node) → Fragment
// Create a new fragment by prepending the given node to this
// fragment.
Fragment.prototype.addToStart = function (node) {
return new Fragment([node].concat(this.content), this.size + node.nodeSize);
};
// : (Node) → Fragment
// Create a new fragment by appending the given node to this
// fragment.
Fragment.prototype.addToEnd = function (node) {
return new Fragment(this.content.concat(node), this.size + node.nodeSize);
};
// :: (Fragment) → bool
// Compare this fragment to another one.
Fragment.prototype.eq = function (other) {
var this$1 = this;
if (this.content.length != other.content.length) {
return false;
}
for (var i = 0; i < this.content.length; i++) {
if (!this$1.content[i].eq(other.content[i])) {
return false;
}
}
return true;
};
// :: ?Node
// The first child of the fragment, or `null` if it is empty.
prototypeAccessors.firstChild.get = function () {
return this.content.length ? this.content[0] : null;
};
// :: ?Node
// The last child of the fragment, or `null` if it is empty.
prototypeAccessors.lastChild.get = function () {
return this.content.length ? this.content[this.content.length - 1] : null;
};
// :: number
// The number of child nodes in this fragment.
prototypeAccessors.childCount.get = function () {
return this.content.length;
};
// :: (number) → Node
// Get the child node at the given index. Raise an error when the
// index is out of range.
Fragment.prototype.child = function (index) {
var found = this.content[index];
if (!found) {
throw new RangeError("Index " + index + " out of range for " + this);
}
return found;
};
// :: (number) → number
// Get the offset at (size of children before) the given index.
Fragment.prototype.offsetAt = function (index) {
var this$1 = this;
var offset = 0;
for (var i = 0; i < index; i++) {
offset += this$1.content[i].nodeSize;
}
return offset;
};
// :: (number) → ?Node
// Get the child node at the given index, if it exists.
Fragment.prototype.maybeChild = function (index) {
return this.content[index];
};
// :: ((node: Node, offset: number, index: number))
// Call `f` for every child node, passing the node, its offset
// into this parent node, and its index.
Fragment.prototype.forEach = function (f) {
var this$1 = this;
for (var i = 0, p = 0; i < this.content.length; i++) {
var child = this$1.content[i];
f(child, p, i);
p += child.nodeSize;
}
};
// :: (Fragment) → ?number
// Find the first position at which this fragment and another
// fragment differ, or `null` if they are the same.
Fragment.prototype.findDiffStart = function (other, pos) {
if (pos === void 0) pos = 0;
return findDiffStart(this, other, pos);
};
// :: (Node) → ?{a: number, b: number}
// 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.
Fragment.prototype.findDiffEnd = function (other, pos, otherPos) {
if (pos === void 0) pos = this.size;
if (otherPos === void 0) otherPos = other.size;
return findDiffEnd(this, other, pos, otherPos);
};
// : (number, ?number) → {index: number, offset: number}
// 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. (Not public.)
Fragment.prototype.findIndex = function (pos, round) {
var this$1 = this;
if (round === void 0) round = -1;
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 (var i = 0, curPos = 0;; i++) {
var cur = this$1.child(i),
end = curPos + cur.nodeSize;
if (end >= pos) {
if (end == pos || round > 0) {
return retIndex(i + 1, end);
}
return retIndex(i, curPos);
}
curPos = end;
}
};
// :: () → string
// Return a debugging string that describes this fragment.
Fragment.prototype.toString = function () {
return "<" + this.toStringInner() + ">";
};
Fragment.prototype.toStringInner = function () {
return this.content.join(", ");
};
// :: () → ?Object
// Create a JSON-serializeable representation of this fragment.
Fragment.prototype.toJSON = function () {
return this.content.length ? this.content.map(function (n) {
return n.toJSON();
}) : null;
};
// :: (Schema, ?Object) → Fragment
// Deserialize a fragment from its JSON representation.
Fragment.fromJSON = function (schema, value) {
return value ? new Fragment(value.map(schema.nodeFromJSON)) : Fragment.empty;
};
// :: ([Node]) → Fragment
// Build a fragment from an array of nodes. Ensures that adjacent
// text nodes with the same style are joined together.
Fragment.fromArray = function (array) {
if (!array.length) {
return Fragment.empty;
}
var joined,
size = 0;
for (var i = 0; i < array.length; i++) {
var 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.withText(joined[joined.length - 1].text + node.text);
} else if (joined) {
joined.push(node);
}
}
return new Fragment(joined || array, size);
};
// :: (?union<Fragment, Node, [Node]>) → Fragment
// 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.
Fragment.from = function (nodes) {
if (!nodes) {
return Fragment.empty;
}
if (nodes instanceof Fragment) {
return nodes;
}
if (Array.isArray(nodes)) {
return this.fromArray(nodes);
}
return new Fragment([nodes], nodes.nodeSize);
};
Object.defineProperties(Fragment.prototype, prototypeAccessors);
exports.Fragment = Fragment;
var found = { index: 0, offset: 0 };
function retIndex(index, offset) {
found.index = index;
found.offset = offset;
return found;
}
// :: Fragment
// 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).
Fragment.empty = new Fragment([], 0);
},{"./diff":4}],6:[function(require,module,exports){
var ref = require("./fragment");
var Fragment = ref.Fragment;
var ref$1 = require("./replace");
var Slice = ref$1.Slice;
var ref$2 = require("./mark");
var Mark = ref$2.Mark;
// ParseOptions:: interface
// Set of options for parsing a DOM node.
//
// preserveWhitespace:: ?union<bool, "full">
// By default, whitespace is collapsed as per HTML's rules. Pass
// `true` to preserve whitespace, but normalize newlines to
// spaces, and `"full"` to preserve whitespace entirely.
//
// findPositions:: ?[{node: dom.Node, offset: number}]
// 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.
//
// from:: ?number
// The child node index to start parsing from.
//
// to:: ?number
// The child node index to stop parsing at.
//
// topNode:: ?Node
// 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.
//
// topStart:: ?number
// Can be used to influence the content match at the start of
// the topnode. When given, should be a valid index into
// `topNode`.
//
// context:: ?ResolvedPos
// A set of additional node names to count as
// [context](#model.ParseRule.context) when parsing, above the
// given [top node](#model.DOMParser.parse^options.topNode).
// ParseRule:: interface
// A value that describes how to parse a given DOM node or inline
// style as a ProseMirror node or mark.
//
// tag:: ?string
// A CSS selector describing the kind of DOM elements to match. A
// single rule should have _either_ a `tag` or a `style` property.
//
// namespace:: ?string
// The namespace to match. This should be used with `tag`.
// Nodes are only matched when the namespace matches or this property
// is null.
//
// style:: ?string
// A CSS property name to match. When given, this rule matches
// inline styles that list that property.
//
// context:: ?string
// 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.
//
// node:: ?string
// The name of the node type to create when this rule matches. Only
// valid for rules with a `tag` property, not for style rules. Each
// rule should have one of a `node`, `mark`, or `ignore` property
// (except when it appears in a [node](#model.NodeSpec.parseDOM) or
// [mark spec](#model.MarkSpec.parseDOM), in which case the `node`
// or `mark` property will be derived from its position).
//
// mark:: ?string
// The name of the mark type to wrap the matched content in.
//
// priority:: ?number
// 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.
//
// ignore:: ?bool
// When true, ignore content that matches this rule.
//
// skip:: ?bool
// When true, ignore the node that matches this rule, but do parse
// its content.
//
// attrs:: ?Object
// Attributes for the node or mark created by this rule. When
// `getAttrs` is provided, it takes precedence.
//
// getAttrs:: ?(union<dom.Node, string>) → ?union<bool, Object>
// A function used to compute the attributes for the node or mark
// created by this rule. Can also be used to describe further
// conditions the DOM element or style must match. When it returns
// `false`, the rule won't match. When it returns null or undefined,
// that is interpreted as an empty/default set of attributes.
//
// Called with a DOM Element for `tag` rules, and with a string (the
// style's value) for `style` rules.
//
// contentElement:: ?string
// For `tag` rules that produce non-leaf nodes or marks, by default
// the content of the DOM element is parsed as content of the mark
// or node. If the child nodes are in a descendent node, this may be
// a CSS selector string that the parser must use to find the actual
// content element.
//
// getContent:: ?(dom.Node) → Fragment
// Can be used to override the content of a matched node. Will be
// called, and its result used, instead of parsing the node's child
// nodes.
//
// preserveWhitespace:: ?union<bool, "full">
// Controls whether whitespace should be preserved when parsing the
// content inside the matched element. `false` means whitespace may
// be collapsed, `true` means that whitespace should be preserved
// but newlines normalized to spaces, and `"full"` means that
// newlines should also be preserved.
// ::- A DOM parser represents a strategy for parsing DOM content into
// a ProseMirror document conforming to a given schema. Its behavior
// is defined by an array of [rules](#model.ParseRule).
var DOMParser = function (schema, rules) {
var this$1 = this;
// :: Schema
this.schema = schema;
// :: [ParseRule]
this.rules = rules;
this.tags = [];
this.styles = [];
rules.forEach(function (rule) {
if (rule.tag) {
this$1.tags.push(rule);
} else if (rule.style) {
this$1.styles.push(rule);
}
});
};
// :: (dom.Node, ?ParseOptions) → Node
// Parse a document from the content of a DOM node.
DOMParser.prototype.parse = function (dom, options) {
if (options === void 0) options = {};
var context = new ParseContext(this, options, false);
context.addAll(dom, null, options.from, options.to);
return context.finish();
};
// :: (dom.Node, ?ParseOptions) → Slice
// Parses the content of the given DOM node, like
// [`parse`](#model.DOMParser.parse), and takes the same set of
// options. But unlike that method, which produces a whole node,
// this one returns a slice that is open at the sides, meaning that
// the schema constraints aren't applied to the start of nodes to
// the left of the input and the end of nodes at the end.
DOMParser.prototype.parseSlice = function (dom, options) {
if (options === void 0) options = {};
var context = new ParseContext(this, options, true);
context.addAll(dom, null, options.from, options.to);
return Slice.maxOpen(context.finish());
};
DOMParser.prototype.matchTag = function (dom, context) {
var this$1 = this;
for (var i = 0; i < this.tags.length; i++) {
var rule = this$1.tags[i];
if (matches(dom, rule.tag) && (!rule.namespace || dom.namespaceURI == rule.namespace) && (!rule.context || context.matchesContext(rule.context))) {
if (rule.getAttrs) {
var result = rule.getAttrs(dom);
if (result === false) {
continue;
}
rule.attrs = result;
}
return rule;
}
}
};
DOMParser.prototype.matchStyle = function (prop, value, context) {
var this$1 = this;
for (var i = 0; i < this.styles.length; i++) {
var rule = this$1.styles[i];
if (rule.style == prop && (!rule.context || context.matchesContext(rule.context))) {
if (rule.getAttrs) {
var result = rule.getAttrs(value);
if (result === false) {
continue;
}
rule.attrs = result;
}
return rule;
}
}
};
// :: (Schema) → [ParseRule]
// Extract the parse rules listed in a schema's [node
// specs](#model.NodeSpec.parseDOM).
DOMParser.schemaRules = function (schema) {
var result = [];
function insert(rule) {
var priority = rule.priority == null ? 50 : rule.priority,
i = 0;
for (; i < result.length; i++) {
var next = result[i],
nextPriority = next.priority == null ? 50 : next.priority;
if (nextPriority < priority) {
break;
}
}
result.splice(i, 0, rule);
}
var loop = function (name) {
var rules = schema.marks[name].spec.parseDOM;
if (rules) {
rules.forEach(function (rule) {
insert(rule = copy(rule));
rule.mark = name;
});
}
};
for (var name in schema.marks) loop(name);
var loop$1 = function (name) {
var rules$1 = schema.nodes[name$1].spec.parseDOM;
if (rules$1) {
rules$1.forEach(function (rule) {
insert(rule = copy(rule));
rule.node = name$1;
});
}
};
for (var name$1 in schema.nodes) loop$1(name);
return result;
};
// :: (Schema) → DOMParser
// Construct a DOM parser using the parsing rules listed in a
// schema's [node specs](#model.NodeSpec.parseDOM).
DOMParser.fromSchema = function (schema) {
return schema.cached.domParser || (schema.cached.domParser = new DOMParser(schema, DOMParser.schemaRules(schema)));
};
exports.DOMParser = DOMParser;
// : Object<bool> The block-level tags in HTML5
var blockTags = {
address: true, article: true, aside: true, blockquote: true, canvas: true,
dd: true, div: true, dl: true, fieldset: true, figcaption: true, figure: true,
footer: true, form: true, h1: true, h2: true, h3: true, h4: true, h5: true,
h6: true, header: true, hgroup: true, hr: true, li: true, noscript: true, ol: true,
output: true, p: true, pre: true, section: true, table: true, tfoot: true, ul: true
// : Object<bool> The tags that we normally ignore.
};var ignoreTags = {
head: true, noscript: true, object: true, script: true, style: true, title: true
// : Object<bool> List tags.
};var listTags = { ol: true, ul: true
// Using a bitfield for node context options
};var OPT_PRESERVE_WS = 1,
OPT_PRESERVE_WS_FULL = 2,
OPT_OPEN_LEFT = 4;
function wsOptionsFor(preserveWhitespace) {
return (preserveWhitespace ? OPT_PRESERVE_WS : 0) | (preserveWhitespace === "full" ? OPT_PRESERVE_WS_FULL : 0);
}
var NodeContext = function (type, attrs, solid, match, options) {
this.type = type;
this.attrs = attrs;
this.solid = solid;
this.match = match || (options & OPT_OPEN_LEFT ? null : type.contentExpr.start(attrs));
this.options = options;
this.content = [];
};
NodeContext.prototype.findWrapping = function (type, attrs) {
if (!this.match) {
if (!this.type) {
return [];
}
var found = this.type.contentExpr.atType(this.attrs, type, attrs);
if (!found) {
var start = this.type.contentExpr.start(this.attrs),
wrap;
if (wrap = start.findWrapping(type, attrs)) {
this.match = start;
return wrap;
}
}
if (found) {
this.match = found;
} else {
return null;
}
}
return this.match.findWrapping(type, attrs);
};
NodeContext.prototype.finish = function (openEnd) {
if (!(this.options & OPT_PRESERVE_WS)) {
// Strip trailing whitespace
var last = this.content[this.content.length - 1],
m;
if (last && last.isText && (m = /\s+$/.exec(last.text))) {
if (last.text.length == m[0].length) {
this.content.pop();
} else {
this.content[this.content.length - 1] = last.withText(last.text.slice(0, last.text.length - m[0].length));
}
}
}
var content = Fragment.from(this.content);
if (!openEnd && this.match) {
content = content.append(this.match.fillBefore(Fragment.empty, true));
}
return this.type ? this.type.create(this.attrs, content) : content;
};
var ParseContext = function (parser, options, open) {
// : DOMParser The parser we are using.
this.parser = parser;
// : Object The options passed to this parse.
this.options = options;
this.isOpen = open;
var topNode = options.topNode,
topContext;
var topOptions = wsOptionsFor(options.preserveWhitespace) | (open ? OPT_OPEN_LEFT : 0);
if (topNode) {
topContext = new NodeContext(topNode.type, topNode.attrs, true, topNode.contentMatchAt(options.topStart || 0), topOptions);
} else if (open) {
topContext = new NodeContext(null, null, true, null, topOptions);
} else {
topContext = new NodeContext(parser.schema.topNodeType, null, true, null, topOptions);
}
this.nodes = [topContext];
// : [Mark] The current set of marks
this.marks = Mark.none;
this.open = 0;
this.find = options.findPositions;
this.needsBlock = false;
};
var prototypeAccessors = { top: {}, currentPos: {} };
prototypeAccessors.top.get = function () {
return this.nodes[this.open];
};
// : (Mark) → [Mark]
// Add a mark to the current set of marks, return the old set.
ParseContext.prototype.addMark = function (mark) {
var old = this.marks;
this.marks = mark.addToSet(this.marks);
return old;
};
// : (dom.Node)
// Add a DOM node to the content. Text is inserted as text node,
// otherwise, the node is passed to `addElement` or, if it has a
// `style` attribute, `addElementWithStyles`.
ParseContext.prototype.addDOM = function (dom) {
if (dom.nodeType == 3) {
this.addTextNode(dom);
} else if (dom.nodeType == 1) {
var style = dom.getAttribute("style");
if (style) {
this.addElementWithStyles(parseStyles(style), dom);
} else {
this.addElement(dom);
}
}
};
ParseContext.prototype.addTextNode = function (dom) {
var value = dom.nodeValue;
var top = this.top;
if (top.type && top.type.inlineContent || /\S/.test(value)) {
if (!(top.options & OPT_PRESERVE_WS)) {
value = value.replace(/\s+/g, " ");
// If this starts with whitespace, and there is either no node
// before it or a node that ends with whitespace, strip the
// leading space.
if (/^\s/.test(value) && this.open == this.nodes.length - 1) {
var nodeBefore = top.content[top.content.length - 1];
if (!nodeBefore || nodeBefore.isText && /\s$/.test(nodeBefore.text)) {
value = value.slice(1);
}
}
} else if (!(top.options & OPT_PRESERVE_WS_FULL)) {
value = value.replace(/\r?\n|\r/g, " ");
}
if (value) {
this.insertNode(this.parser.schema.text(value, this.marks));
}
this.findInText(dom);
} else {
this.findInside(dom);
}
};
// : (dom.Element)
// Try to find a handler for the given tag and use that to parse. If
// none is found, the element's content nodes are added directly.
ParseContext.prototype.addElement = function (dom) {
var name = dom.nodeName.toLowerCase();
if (listTags.hasOwnProperty(name)) {
normalizeList(dom);
}
var rule = this.options.ruleFromNode && this.options.ruleFromNode(dom) || this.parser.matchTag(dom, this);
if (rule ? rule.ignore : ignoreTags.hasOwnProperty(name)) {
this.findInside(dom);
} else if (!rule || rule.skip) {
if (rule && rule.skip.nodeType) {
dom = rule.skip;
}
var sync,
oldNeedsBlock = this.needsBlock;
if (blockTags.hasOwnProperty(name)) {
sync = this.top;
if (!sync.type) {
this.needsBlock = true;
}
}
this.addAll(dom);
if (sync) {
this.sync(sync);
}
this.needsBlock = oldNeedsBlock;
} else {
this.addElementByRule(dom, rule);
}
};
// Run any style parser associated with the node's styles. After
// that, if no style parser suppressed the node's content, pass it
// through to `addElement`.
ParseContext.prototype.addElementWithStyles = function (styles, dom) {
var this$1 = this;
var oldMarks = this.marks,
ignore = false;
for (var i = 0; i < styles.length; i += 2) {
var rule = this$1.parser.matchStyle(styles[i], styles[i + 1], this$1);
if (!rule) {
continue;
}
if (rule.ignore) {
ignore = true;break;
}
this$1.addMark(this$1.parser.schema.marks[rule.mark].create(rule.attrs));
}
if (!ignore) {
this.addElement(dom);
}
this.marks = oldMarks;
};
// : (dom.Element, ParseRule) → bool
// Look up a handler for the given node. If none are found, return
// false. Otherwise, apply it, use its return value to drive the way
// the node's content is wrapped, and return true.
ParseContext.prototype.addElementByRule = function (dom, rule) {
var this$1 = this;
var sync, before, nodeType, markType, mark;
if (rule.node) {
nodeType = this.parser.schema.nodes[rule.node];
if (nodeType.isLeaf) {
this.insertNode(nodeType.create(rule.attrs, null, this.marks));
} else {
sync = this.enter(nodeType, rule.attrs, rule.preserveWhitespace) && this.top;
}
} else {
markType = this.parser.schema.marks[rule.mark];
before = this.addMark(mark = markType.create(rule.attrs));
}
if (nodeType && nodeType.isLeaf) {
this.findInside(dom);
} else if (rule.getContent) {
this.findInside(dom);
rule.getContent(dom).forEach(function (node) {
return this$1.insertNode(mark ? node.mark(mark.addToSet(node.marks)) : node);
});
} else {
var contentDOM = rule.contentElement;
if (typeof contentDOM == "string") {
contentDOM = dom.querySelector(contentDOM);
}
if (!contentDOM) {
contentDOM = dom;
}
this.findAround(dom, contentDOM, true);
this.addAll(contentDOM, sync);
}
if (sync) {
this.sync(sync);this.open--;
} else if (before) {
this.marks = before;
}
return true;
};
// : (dom.Node, ?NodeBuilder, ?number, ?number)
// Add all child nodes between `startIndex` and `endIndex` (or the
// whole node, if not given). If `sync` is passed, use it to
// synchronize after every block element.
ParseContext.prototype.addAll = function (parent, sync, startIndex, endIndex) {
var this$1 = this;
var index = startIndex || 0;
for (var dom = startIndex ? parent.childNodes[startIndex] : parent.firstChild, end = endIndex == null ? null : parent.childNodes[endIndex]; dom != end; dom = dom.nextSibling, ++index) {
this$1.findAtPoint(parent, index);
this$1.addDOM(dom);
if (sync && blockTags.hasOwnProperty(dom.nodeName.toLowerCase())) {
this$1.sync(sync);
}
}
this.findAtPoint(parent, index);
};
// Try to find a way to fit the given node type into the current
// context. May add intermediate wrappers and/or leave non-solid
// nodes that we're in.
ParseContext.prototype.findPlace = function (type, attrs) {
var this$1 = this;
var route, sync;
for (var depth = this.open; depth >= 0; depth--) {
var node = this$1.nodes[depth];
var found = node.findWrapping(type, attrs);
if (found && (!route || route.length > found.length)) {
route = found;
sync = node;
if (!found.length) {
break;
}
}
if (node.solid) {
break;
}
}
if (!route) {
return false;
}
this.sync(sync);
for (var i = 0; i < route.length; i++) {
this$1.enterInner(route[i].type, route[i].attrs, false);
}
return true;
};
// : (Node) → ?Node
// Try to insert the given node, adjusting the context when needed.
ParseContext.prototype.insertNode = function (node) {
if (node.isInline && this.needsBlock && !this.top.type) {
var block = this.textblockFromContext();
if (block) {
this.enter(block);
}
}
if (this.findPlace(node.type, node.attrs)) {
this.closeExtra();
var top = this.top;
if (top.match) {
var match = top.match.matchNode(node);
if (!match) {
node = node.mark(node.marks.filter(function (mark) {
return top.match.allowsMark(mark.type);
}));
match = top.match.matchNode(node);
}
top.match = match;
}
top.content.push(node);
}
};
// : (NodeType, ?Object) → bool
// Try to start a node of the given type, adjusting the context when
// necessary.
ParseContext.prototype.enter = function (type, attrs, preserveWS) {
var ok = this.findPlace(type, attrs);
if (ok) {
this.enterInner(type, attrs, true, preserveWS);
}
return ok;
};
// Open a node of the given type
ParseContext.prototype.enterInner = function (type, attrs, solid, preserveWS) {
this.closeExtra();
var top = this.top;
top.match = top.match && top.match.matchType(type, attrs);
var options = preserveWS == null ? top.options & ~OPT_OPEN_LEFT : wsOptionsFor(preserveWS);
if (top.options & OPT_OPEN_LEFT && top.content.length == 0) {
options |= OPT_OPEN_LEFT;
}
this.nodes.push(new NodeContext(type, attrs, solid, null, options));
this.open++;
};
// Make sure all nodes above this.open are finished and added to
// their parents
ParseContext.prototype.closeExtra = function (openEnd) {
var this$1 = this;
var i = this.nodes.length - 1;
if (i > this.open) {
this.marks = Mark.none;
for (; i > this.open; i--) {
this$1.nodes[i - 1].content.push(this$1.nodes[i].finish(openEnd));
}
this.nodes.length = this.open + 1;
}
};
ParseContext.prototype.finish = function () {
this.open = 0;
this.closeExtra(this.isOpen);
return this.nodes[0].finish(this.isOpen || this.options.topOpen);
};
ParseContext.prototype.sync = function (to) {
var this$1 = this;
for (var i = this.open; i >= 0; i--) {
if (this$1.nodes[i] == to) {
this$1.open = i;
return;
}
}
};
prototypeAccessors.currentPos.get = function () {
var this$1 = this;
this.closeExtra();
var pos = 0;
for (var i = this.open; i >= 0; i--) {
var content = this$1.nodes[i].content;
for (var j = content.length - 1; j >= 0; j--) {
pos += content[j].nodeSize;
}
if (i) {
pos++;
}
}
return pos;
};
ParseContext.prototype.findAtPoint = function (parent, offset) {
var this$1 = this;
if (this.find) {
for (var i = 0; i < this.find.length; i++) {
if (this$1.find[i].node == parent && this$1.find[i].offset == offset) {
this$1.find[i].pos = this$1.currentPos;
}
}
}
};
ParseContext.prototype.findInside = function (parent) {
var this$1 = this;
if (this.find) {
for (var i = 0; i < this.find.length; i++) {
if (this$1.find[i].pos == null && parent.nodeType == 1 && parent.contains(this$1.find[i].node)) {
this$1.find[i].pos = this$1.currentPos;
}
}
}
};
ParseContext.prototype.findAround = function (parent, content, before) {
var this$1 = this;
if (parent != content && this.find) {
for (var i = 0; i < this.find.length; i++) {
if (this$1.find[i].pos == null && parent.nodeType == 1 && parent.contains(this$1.find[i].node)) {
var pos = content.compareDocumentPosition(this$1.find[i].node);
if (pos & (before ? 2 : 4)) {
this$1.find[i].pos = this$1.currentPos;
}
}
}
}
};
ParseContext.prototype.findInText = function (textNode) {
var this$1 = this;
if (this.find) {
for (var i = 0; i < this.find.length; i++) {
if (this$1.find[i].node == textNode) {
this$1.find[i].pos = this$1.currentPos - (textNode.nodeValue.length - this$1.find[i].offset);
}
}
}
};
// : (string) → bool
// Determines whether the given [context
// string](#ParseRule.context) matches this context.
ParseContext.prototype.matchesContext = function (context) {
var this$1 = this;
var parts = context.split("/");
var option = this.options.context;
var useRoot = !this.isOpen && (!option || option.parent.type == this.nodes[0].type);
var minDepth = -(option ? option.depth + 1 : 0) + (useRoot ? 0 : 1);
var match = function (i, depth) {
for (; i >= 0; i--) {
var part = parts[i];
if (part == "") {
if (i == parts.length - 1 || i == 0) {
continue;
}
for (; depth >= minDepth; depth--) {
if (match(i - 1, depth)) {
return true;
}
}
return false;
} else {
var next = depth > 0 || depth == 0 && useRoot ? this$1.nodes[depth].type : option && depth >= minDepth ? option.node(depth - minDepth).type : null;
if (!next || next.name != part && next.groups.indexOf(part) == -1) {
return false;
}
depth--;
}
}
return true;
};
return match(parts.length - 1, this.open);
};
ParseContext.prototype.textblockFromContext = function () {
var this$1 = this;
var $context = this.options.context;
if ($context) {
for (var d = $context.depth; d >= 0; d--) {
var deflt = $context.node(d).defaultContentType($context.indexAfter(d));
if (deflt && deflt.isTextblock && deflt.defaultAttrs) {
return deflt;
}
}
}
for (var name in this$1.parser.schema.nodes) {
var type = this$1.parser.schema.nodes[name];
if (type.isTextblock && type.defaultAttrs) {
return type;
}
}
};
Object.defineProperties(ParseContext.prototype, prototypeAccessors);
// Kludge to work around directly nested list nodes produced by some
// tools and allowed by browsers to mean that the nested list is
// actually part of the list item above it.
function normalizeList(dom) {
for (var child = dom.firstChild, prevItem = null; child; child = child.nextSibling) {
var name = child.nodeType == 1 ? child.nodeName.toLowerCase() : null;
if (name && listTags.hasOwnProperty(name) && prevItem) {
prevItem.appendChild(child);
child = prevItem;
} else if (name == "li") {
prevItem = child;
} else if (name) {
prevItem = null;
}
}
}
// Apply a CSS selector.
function matches(dom, selector) {
return (dom.matches || dom.msMatchesSelector || dom.webkitMatchesSelector || dom.mozMatchesSelector).call(dom, selector);
}
// : (string) → [string]
// Tokenize a style attribute into property/value pairs.
function parseStyles(style) {
var re = /\s*([\w-]+)\s*:\s*([^;]+)/g,
m,
result = [];
while (m = re.exec(style)) {
result.push(m[1], m[2].trim());
}
return result;
}
function copy(obj) {
var copy = {};
for (var prop in obj) {
copy[prop] = obj[prop];
}
return copy;
}
},{"./fragment":5,"./mark":8,"./replace":10}],7:[function(require,module,exports){
exports.Node = require("./node").Node;var assign;
assign = require("./resolvedpos"), exports.ResolvedPos = assign.ResolvedPos, exports.NodeRange = assign.NodeRange;
exports.Fragment = require("./fragment").Fragment;var assign$1;
assign$1 = require("./replace"), exports.Slice = assign$1.Slice, exports.ReplaceError = assign$1.ReplaceError;
exports.Mark = require("./mark").Mark;var assign$2;
assign$2 = require("./schema"), exports.Schema = assign$2.Schema, exports.NodeType = assign$2.NodeType, exports.MarkType = assign$2.MarkType;var assign$3;
assign$3 = require("./content"), exports.ContentMatch = assign$3.ContentMatch;
exports.DOMParser = require("./from_dom").DOMParser;
exports.DOMSerializer = require("./to_dom").DOMSerializer;
},{"./content":3,"./fragment":5,"./from_dom":6,"./mark":8,"./node":9,"./replace":10,"./resolvedpos":11,"./schema":12,"./to_dom":13}],8:[function(require,module,exports){
var ref = require("./comparedeep");
var compareDeep = ref.compareDeep;
// ::- A mark is a piece of information that can be attached to a node,
// such as it being emphasized, in code font, or a link. It has a type
// and optionally a set of attributes that provide further information
// (such as the target of the link). Marks are created through a
// `Schema`, which controls which types exist and which
// attributes they have.
var Mark = function (type, attrs) {
// :: MarkType
// The type of this mark.
this.type = type;
// :: Object
// The attributes associated with this mark.
this.attrs = attrs;
};
// :: ([Mark]) → [Mark]
// Given a set of marks, create a new set which contains this one as
// well, in the right position. If this mark is already in the set,
// the set itself is returned. If a mark of this type with different
// attributes is already in the set, a set in which it is replaced
// by this one is returned.
Mark.prototype.addToSet = function (set) {
var this$1 = this;
var copy,
placed = false;
for (var i = 0; i < set.length; i++) {
var other = set[i];
if (this$1.eq(other)) {
return set;
}
if (this$1.type.excludes(other.type)) {
if (!copy) {
copy = set.slice(0, i);
}
} else if (other.type.excludes(this$1.type)) {
return set;
} else {
if (!placed && other.type.rank > this$1.type.rank) {
if (!copy) {
copy = set.slice(0, i);
}
copy.push(this$1);
placed = true;
}
if (copy) {
copy.push(other);
}
}
}
if (!copy) {
copy = set.slice();
}
if (!placed) {
copy.push(this);
}
return copy;
};
// :: ([Mark]) → [Mark]
// Remove this mark from the given set, returning a new set. If this
// mark is not in the set, the set itself is returned.
Mark.prototype.removeFromSet = function (set) {
var this$1 = this;
for (var i = 0; i < set.length; i++) {
if (this$1.eq(set[i])) {
return set.slice(0, i).concat(set.slice(i + 1));
}
}
return set;
};
// :: ([Mark]) → bool
// Test whether this mark is in the given set of marks.
Mark.prototype.isInSet = function (set) {
var this$1 = this;
for (var i = 0; i < set.length; i++) {
if (this$1.eq(set[i])) {
return true;
}
}
return false;
};
// :: (Mark) → bool
// Test whether this mark has the same type and attributes as
// another mark.
Mark.prototype.eq = function (other) {
return this == other || this.type == other.type && compareDeep(this.attrs, other.attrs);
};
// :: () → Object
// Convert this mark to a JSON-serializeable representation.
Mark.prototype.toJSON = function () {
var this$1 = this;
var obj = { type: this.type.name };
for (var _ in this$1.attrs) {
obj.attrs = this$1.attrs;
break;
}
return obj;
};
// :: (Schema, Object) → Mark
Mark.fromJSON = function (schema, json) {
var type = schema.marks[json.type];
if (!type) {
throw new RangeError("There is no mark type " + json.type + " in this schema");
}
return type.create(json.attrs);
};
// :: ([Mark], [Mark]) → bool
// Test whether two sets of marks are identical.
Mark.sameSet = function (a, b) {
if (a == b) {
return true;
}
if (a.length != b.length) {
return false;
}
for (var i = 0; i < a.length; i++) {
if (!a[i].eq(b[i])) {
return false;
}
}
return true;
};
// :: (?union<Mark, [Mark]>) → [Mark]
// Create a properly sorted mark set from null, a single mark, or an
// unsorted array of marks.
Mark.setFrom = function (marks) {
if (!marks || marks.length == 0) {
return Mark.none;
}
if (marks instanceof Mark) {
return [marks];
}
var copy = marks.slice();
copy.sort(function (a, b) {
return a.type.rank - b.type.rank;
});
return copy;
};
exports.Mark = Mark;
// :: [Mark] The empty set of marks.
Mark.none = [];
},{"./comparedeep":2}],9:[function(require,module,exports){
var ref = require("./fragment");
var Fragment = ref.Fragment;
var ref$1 = require("./mark");
var Mark = ref$1.Mark;
var ref$2 = require("./replace");
var Slice = ref$2.Slice;
var replace = ref$2.replace;
var ref$3 = require("./resolvedpos");
var ResolvedPos = ref$3.ResolvedPos;
var ref$4 = require("./comparedeep");
var compareDeep = ref$4.compareDeep;
var emptyAttrs = Object.create(null);
// ::- This class represents a node in the tree that makes up a
// ProseMirror document. So a document is an instance of `Node`, with
// children that are also instances of `Node`.
//
// Nodes are persistent data structures. Instead of changing them, you
// create new ones with the content you want. Old ones keep pointing
// at the old document shape. This is made cheaper by sharing
// structure between the old and new data as much as possible, which a
// tree shape like this (without back pointers) makes easy.
//
// **Never** directly mutate the properties of a `Node` object. See
// [this guide](/docs/guides/doc/) for more information.
var Node = function (type, attrs, content, marks) {
// :: NodeType
// The type of node that this is.
this.type = type;
// :: Object
// An object mapping attribute names to values. The kind of
// attributes allowed and required are determined by the node
// type.
this.attrs = attrs;
// :: Fragment
// A container holding the node's children.
this.content = content || Fragment.empty;
// :: [Mark]
// The marks (things like whether it is emphasized or part of a
// link) associated with this node.
this.marks = marks || Mark.none;
};
var prototypeAccessors = { nodeSize: {}, childCount: {}, textContent: {}, firstChild: {}, lastChild: {}, isBlock: {}, isTextblock: {}, inlineContent: {}, isInline: {}, isText: {}, isLeaf: {}, isAtom: {} };
// text:: ?string
// For text nodes, this contains the node's text content.
// :: number
// The size of this node, as defined by the integer-based [indexing
// scheme](/docs/guides/doc/#indexing). For text nodes, this is the
// amount of characters. For other leaf nodes, it is one. And for
// non-leaf nodes, it is the size of the content plus two (the start
// and end token).
prototypeAccessors.nodeSize.get = function () {
return this.isLeaf ? 1 : 2 + this.content.size;
};
// :: number
// The number of children that the node has.
prototypeAccessors.childCount.get = function () {
return this.content.childCount;
};
// :: (number) → Node
// Get the child node at the given index. Raises an error when the
// index is out of range.
Node.prototype.child = function (index) {
return this.content.child(index);
};
// :: (number) → ?Node
// Get the child node at the given index, if it exists.
Node.prototype.maybeChild = function (index) {
return this.content.maybeChild(index);
};
// :: ((node: Node, offset: number, index: number))
// Call `f` for every child node, passing the node, its offset
// into this parent node, and its index.
Node.prototype.forEach = function (f) {
this.content.forEach(f);
};
// :: (number, number, (node: Node, pos: number, parent: Node, index: number) → ?bool)
// Invoke a callback for all descendant nodes recursively between
// the given two positions that are relative to start of this node's content.
// The callback is invoked with the node, its parent-relative position,
// its parent node, and its child index. If the callback returns false,
// the current node's children will not be recursed over.
Node.prototype.nodesBetween = function (from, to, f, pos) {
if (pos === void 0) pos = 0;
this.content.nodesBetween(from, to, f, pos, this);
};
// :: ((node: Node, pos: number, parent: Node) → ?bool)
// Call the given callback for every descendant node. If doesn't
// descend into a child node when the callback returns `false`.
Node.prototype.descendants = function (f) {
this.nodesBetween(0, this.content.size, f);
};
// :: string
// Concatenates all the text nodes found in this fragment and its
// children.
prototypeAccessors.textContent.get = function () {
return this.textBetween(0, this.content.size, "");
};
// :: (number, number, ?string, ?string) → string
// Get all text between positions `from` and `to`. When
// `blockSeparator` is given, it will be inserted whenever a new
// block node is started. When `leafText` is given, it'll be
// inserted for every non-text leaf node encountered.
Node.prototype.textBetween = function (from, to, blockSeparator, leafText) {
return this.content.textBetween(from, to, blockSeparator, leafText);
};
// :: ?Node
// Returns this node's first child, or `null` if there are no
// children.
prototypeAccessors.firstChild.get = function () {
return this.content.firstChild;
};
// :: ?Node
// Returns this node's last child, or `null` if there are no
// children.
prototypeAccessors.lastChild.get = function () {
return this.content.lastChild;
};
// :: (Node) → bool
// Test whether two nodes represent the same content.
Node.prototype.eq = function (other) {
return this == other || this.sameMarkup(other) && this.content.eq(other.content);
};
// :: (Node) → bool
// Compare the markup (type, attributes, and marks) of this node to
// those of another. Returns `true` if both have the same markup.
Node.prototype.sameMarkup = function (other) {
return this.hasMarkup(other.type, other.attrs, other.marks);
};
// :: (NodeType, ?Object, ?[Mark]) → bool
// Check whether this node's markup correspond to the given type,
// attributes, and marks.
Node.prototype.hasMarkup = function (type, attrs, marks) {
return this.type == type && compareDeep(this.attrs, attrs || type.defaultAttrs || emptyAttrs) && Mark.sameSet(this.marks, marks || Mark.none);
};
// :: (?Fragment) → Node
// Create a new node with the same markup as this node, containing
// the given content (or empty, if no content is given).
Node.prototype.copy = function (content) {
if (content === void 0) content = null;
if (content == this.content) {
return this;
}
return new this.constructor(this.type, this.attrs, content, this.marks);
};
// :: ([Mark]) → Node
// Create a copy of this node, with the given set of marks instead
// of the node's own marks.
Node.prototype.mark = function (marks) {
return marks == this.marks ? this : new this.constructor(this.type, this.attrs, this.content, marks);
};
// :: (number, ?number) → Node
// Create a copy of this node with only the content between the
// given offsets. If `to` is not given, it defaults to the end of
// the node.
Node.prototype.cut = function (from, to) {
if (from == 0 && to == this.content.size) {
return this;
}
return this.copy(this.content.cut(from, to));
};
// :: (number, ?number) → Slice
// Cut out the part of the document between the given positions, and
// return it as a `Slice` object.
Node.prototype.slice = function (from, to, includeParents) {
if (to === void 0) to = this.content.size;
if (includeParents === void 0) includeParents = false;
if (from == to) {
return Slice.empty;
}
var $from = this.resolve(from),
$to = this.resolve(to);
var depth = includeParents ? 0 : $from.sharedDepth(to);
var start = $from.start(depth),
node = $from.node(depth);
var content = node.content.cut($from.pos - start, $to.pos - start);
return new Slice(content, $from.depth - depth, $to.depth - depth);
};
// :: (number, number, Slice) → Node
// Replace the part of the document between the given positions with
// the given slice. The slice must 'fit', meaning its open sides
// must be able to connect to the surrounding content, and its
// content nodes must be valid children for the node they are placed
// into. If any of this is violated, an error of type
// [`ReplaceError`](#model.ReplaceError) is thrown.
Node.prototype.replace = function (from, to, slice) {
return replace(this.resolve(from), this.resolve(to), slice);
};
// :: (number) → ?Node
// Find the node after the given position.
Node.prototype.nodeAt = function (pos) {
for (var node = this;;) {
var ref = node.content.findIndex(pos);
var index = ref.index;
var offset = ref.offset;
node = node.maybeChild(index);
if (!node) {
return null;
}
if (offset == pos || node.isText) {
return node;
}
pos -= offset + 1;
}
};
// :: (number) → {node: ?Node, index: number, offset: number}
// Find the (direct) child node after the given offset, if any,
// and return it along with its index and offset relative to this
// node.
Node.prototype.childAfter = function (pos) {
var ref = this.content.findIndex(pos);
var index = ref.index;
var offset = ref.offset;
return { node: this.content.maybeChild(index), index: index, offset: offset };
};
// :: (number) → {node: ?Node, index: number, offset: number}
// Find the (direct) child node before the given offset, if any,
// and return it along with its index and offset relative to this
// node.
Node.prototype.childBefore = function (pos) {
if (pos == 0) {
return { node: null, index: 0, offset: 0 };
}
var ref = this.content.findIndex(pos);
var index = ref.index;
var offset = ref.offset;
if (offset < pos) {
return { node: this.content.child(index), index: index, offset: offset };
}
var node = this.content.child(index - 1);
return { node: node, index: index - 1, offset: offset - node.nodeSize };
};
// :: (number) → ResolvedPos
// Resolve the given position in the document, returning an object
// describing its path through the document.
Node.prototype.resolve = function (pos) {
return ResolvedPos.resolveCached(this, pos);
};
Node.prototype.resolveNoCache = function (pos) {
return ResolvedPos.resolve(this, pos);
};
// :: (number, number, MarkType) → bool
// Test whether a mark of the given type occurs in this document
// between the two given positions.
Node.prototype.rangeHasMark = function (from, to, type) {
var found = false;
this.nodesBetween(from, to, function (node) {
if (type.isInSet(node.marks)) {
found = true;
}
return !found;
});
return found;
};
// :: bool
// True when this is a block (non-inline node)
prototypeAccessors.isBlock.get = function () {
return this.type.isBlock;
};
// :: bool
// True when this is a textblock node, a block node with inline
// content.
prototypeAccessors.isTextblock.get = function () {
return this.type.isTextblock;
};
// :: bool
// True when this node has inline content.
prototypeAccessors.inlineContent.get = function () {
return this.type.inlineContent;
};
// :: bool
// True when this is an inline node (a text node or a node that can
// appear among text).
prototypeAccessors.isInline.get = function () {
return this.type.isInline;
};
// :: bool
// True when this is a text node.
prototypeAccessors.isText.get = function () {
return this.type.isText;
};
// :: bool
// True when this is a leaf node.
prototypeAccessors.isLeaf.get = function () {
return this.type.isLeaf;
};
// :: bool
// True when this is an atom, i.e. when it does not have directly
// editable content. This is usually the same as `isLeaf`, but can
// be configured with the [`leaf` property](#model.NodeSpec.leaf) on
// a node's spec (typically when the node is displayed as an
// uneditable [node view](#view.NodeView)).
prototypeAccessors.isAtom.get = function () {
return this.type.isAtom;
};
// :: () → string
// Return a string representation of this node for debugging
// purposes.
Node.prototype.toString = function () {
var name = this.type.name;
if (this.content.size) {
name += "(" + this.content.toStringInner() + ")";
}
return wrapMarks(this.marks, name);
};
// :: (number) → ContentMatch
// Get the content match in this node at the given index.
Node.prototype.contentMatchAt = function (index) {
return this.type.contentExpr.getMatchAt(this.attrs, this.content, index);
};
// :: (number, number, ?Fragment, ?number, ?number) → bool
// Test whether replacing the range `from` to `to` (by index) with
// the given replacement fragment (which defaults to the empty
// fragment) would leave the node's content valid. You can
// optionally pass `start` and `end` indices into the replacement
// fragment.
Node.prototype.canReplace = function (from, to, replacement, start, end) {
return this.type.contentExpr.checkReplace(this.attrs, this.content, from, to, replacement, start, end);
};
// :: (number, number, NodeType, ?[Mark]) → bool
// Test whether replacing the range `from` to `to` (by index) with a
// node of the given type with the given attributes and marks would
// be valid.
Node.prototype.canReplaceWith = function (from, to, type, attrs, marks) {
return this.type.contentExpr.checkReplaceWith(this.attrs, this.content, from, to, type, attrs, marks || Mark.none);
};
// :: (Node) → bool
// Test whether the given node's content could be appended to this
// node. If that node is empty, this will only return true if there
// is at least one node type that can appear in both nodes (to avoid
// merging completely incompatible nodes).
Node.prototype.canAppend = function (other) {
if (other.content.size) {
return this.canReplace(this.childCount, this.childCount, other.content);
} else {
return this.type.compatibleContent(other.type);
}
};
Node.prototype.defaultContentType = function (at) {
var elt = this.contentMatchAt(at).nextElement;
return elt && elt.defaultType();
};
// :: ()
// Check whether this node and its descendants conform to the
// schema, and raise error when they do not.
Node.prototype.check = function () {
if (!this.type.validContent(this.content, this.attrs)) {
throw new RangeError("Invalid content for node " + this.type.name + ": " + this.content.toString().slice(0, 50));
}
this.content.forEach(function (node) {
return node.check();
});
};
// :: () → Object
// Return a JSON-serializeable representation of this node.
Node.prototype.toJSON = function () {
var this$1 = this;
var obj = { type: this.type.name };
for (var _ in this$1.attrs) {
obj.attrs = this$1.attrs;
break;
}
if (this.content.size) {
obj.content = this.content.toJSON();
}
if (this.marks.length) {
obj.marks = this.marks.map(function (n) {
return n.toJSON();
});
}
return obj;
};
// :: (Schema, Object) → Node
// Deserialize a node from its JSON representation.
Node.fromJSON = function (schema, json) {
var marks = json.marks && json.marks.map(schema.markFromJSON);
if (json.type == "text") {
return schema.text(json.text, marks);
}
var type = schema.nodeType(json.type);
if (!type) {
throw new RangeError("There is no node type " + json.type + " in this schema");
}
return type.create(json.attrs, Fragment.fromJSON(schema, json.content), marks);
};
Object.defineProperties(Node.prototype, prototypeAccessors);
exports.Node = Node;
var TextNode = function (Node) {
function TextNode(type, attrs, content, marks) {
Node.call(this, type, attrs, null, marks);
if (!content) {
throw new RangeError("Empty text nodes are not allowed");
}
this.text = content;
}
if (Node) TextNode.__proto__ = Node;
TextNode.prototype = Object.create(Node && Node.prototype);
TextNode.prototype.constructor = TextNode;
var prototypeAccessors$1 = { textContent: {}, nodeSize: {} };
TextNode.prototype.toString = function () {
return wrapMarks(this.marks, JSON.stringify(this.text));
};
prototypeAccessors$1.textContent.get = function () {
return this.text;
};
TextNode.prototype.textBetween = function (from, to) {
return this.text.slice(from, to);
};
prototypeAccessors$1.nodeSize.get = function () {
return this.text.length;
};
TextNode.prototype.mark = function (marks) {
return new TextNode(this.type, this.attrs, this.text, marks);
};
TextNode.prototype.withText = function (text) {
if (text == this.text) {
return this;
}
return new TextNode(this.type, this.attrs, text, this.marks);
};
TextNode.prototype.cut = function (from, to) {
if (from === void 0) from = 0;
if (to === void 0) to = this.text.length;
if (from == 0 && to == this.text.length) {
return this;
}
return this.withText(this.text.slice(from, to));
};
TextNode.prototype.eq = function (other) {
return this.sameMarkup(other) && this.text == other.text;
};
TextNode.prototype.toJSON = function () {
var base = Node.prototype.toJSON.call(this);
base.text = this.text;
return base;
};
Object.defineProperties(TextNode.prototype, prototypeAccessors$1);
return TextNode;
}(Node);
exports.TextNode = TextNode;
function wrapMarks(marks, str) {
for (var i = marks.length - 1; i >= 0; i--) {
str = marks[i].type.name + "(" + str + ")";
}
return str;
}
},{"./comparedeep":2,"./fragment":5,"./mark":8,"./replace":10,"./resolvedpos":11}],10:[function(require,module,exports){
var ref = require("./fragment");
var Fragment = ref.Fragment;
// ::- Error type raised by [`Node.replace`](#model.Node.replace) when
// given an invalid replacement.
var ReplaceError = function (Error) {
function ReplaceError(message) {
Error.call(this, message);
this.message = message;
}
if (Error) ReplaceError.__proto__ = Error;
ReplaceError.prototype = Object.create(Error && Error.prototype);
ReplaceError.prototype.constructor = ReplaceError;
var prototypeAccessors = { name: {} };
prototypeAccessors.name.get = function () {
return "ReplaceError";
};
Object.defineProperties(ReplaceError.prototype, prototypeAccessors);
return ReplaceError;
}(Error);
exports.ReplaceError = ReplaceError;
// ::- A slice represents a piece cut out of a larger document. It
// stores not only a fragment, but also the depth up to which nodes on
// both side are 'open' / cut through.
var Slice = function (content, openStart, openEnd) {
// :: Fragment The slice's content nodes.
this.content = content;
// :: number The open depth at the start.
this.openStart = openStart;
// :: number The open depth at the end.
this.openEnd = openEnd;
};
var prototypeAccessors$1 = { size: {} };
// :: number
// The size this slice would add when inserted into a document.
prototypeAccessors$1.size.get = function () {
return this.content.size - this.openStart - this.openEnd;
};
Slice.prototype.insertAt = function (pos, fragment) {
var content = insertInto(this.content, pos + this.openStart, fragment, null);
return content && new Slice(content, this.openStart, this.openEnd);
};
Slice.prototype.removeBetween = function (from, to) {
return new Slice(removeRange(this.content, from + this.openStart, to + this.openStart), this.openStart, this.openEnd);
};
// :: (Slice) → bool
// Tests whether this slice is equal to another slice.
Slice.prototype.eq = function (other) {
return this.content.eq(other.content) && this.openStart == other.openStart && this.openEnd == other.openEnd;
};
Slice.prototype.toString = function () {
return this.content + "(" + this.openStart + "," + this.openEnd + ")";
};
// :: () → ?Object
// Convert a slice to a JSON-serializable representation.
Slice.prototype.toJSON = function () {
if (!this.content.size) {
return null;
}
var json = { content: this.content.toJSON() };
if (this.openStart > 0) {
json.openStart = this.openStart;
}
if (this.openEnd > 0) {
json.openEnd = this.openEnd;
}
return json;
};
// :: (Schema, ?Object) → Slice
// Deserialize a slice from its JSON representation.
Slice.fromJSON = function (schema, json) {
if (!json) {
return Slice.empty;
}
return new Slice(Fragment.fromJSON(schema, json.content), json.openStart || 0, json.openEnd || 0);
};
// :: (Fragment) → Slice
// Create a slice from a fragment by taking the maximum possible
// open value on both side of the fragment.
Slice.maxOpen = function (fragment) {
var openStart = 0,
openEnd = 0;
for (var n = fragment.firstChild; n && !n.isLeaf; n = n.firstChild) {
openStart++;
}
for (var n$1 = fragment.lastChild; n$1 && !n$1.isLeaf; n$1 = n$1.lastChild) {
openEnd++;
}
return new Slice(fragment, openStart, openEnd);
};
Object.defineProperties(Slice.prototype, prototypeAccessors$1);
exports.Slice = Slice;
function removeRange(content, from, to) {
var ref = content.findIndex(from);
var index = ref.index;
var offset = ref.offset;
var child = content.maybeChild(index);
var ref$1 = content.findIndex(to);
var indexTo = ref$1.index;
var offsetTo = ref$1.offset;
if (offset == from || child.isText) {
if (offsetTo != to && !content.child(indexTo).isText) {
throw new RangeError("Removing non-flat range");
}
return content.cut(0, from).append(content.cut(to));
}
if (index != indexTo) {
throw new RangeError("Removing non-flat range");
}
return content.replaceChild(index, child.copy(removeRange(child.content, from - offset - 1, to - offset - 1)));
}
function insertInto(content, dist, insert, parent) {
var ref = content.findIndex(dist);
var index = ref.index;
var offset = ref.offset;
var child = content.maybeChild(index);
if (offset == dist || child.isText) {
if (parent && !parent.canReplace(index, index, insert)) {
return null;
}
return content.cut(0, dist).append(insert).append(content.cut(dist));
}
var inner = insertInto(child.content, dist - offset - 1, insert);
return inner && content.replaceChild(index, child.copy(inner));
}
// :: Slice
// The empty slice.
Slice.empty = new Slice(Fragment.empty, 0, 0);
function replace($from, $to, slice) {
if (slice.openStart > $from.depth) {
throw new ReplaceError("Inserted content deeper than insertion position");
}
if ($from.depth - slice.openStart != $to.depth - slice.openEnd) {
throw new ReplaceError("Inconsistent open depths");
}
return replaceOuter($from, $to, slice, 0);
}
exports.replace = replace;
function replaceOuter($from, $to, slice, depth) {
var index = $from.index(depth),
node = $from.node(depth);
if (index == $to.index(depth) && depth < $from.depth - slice.openStart) {
var inner = replaceOuter($from, $to, slice, depth + 1);
return node.copy(node.content.replaceChild(index, inner));
} else if (!slice.content.size) {
return close(node, replaceTwoWay($from, $to, depth));
} else if (!slice.openStart && !slice.openEnd && $from.depth == depth && $to.depth == depth) {
// Simple, flat case
var parent = $from.parent,
content = parent.content;
return close(parent, content.cut(0, $from.parentOffset).append(slice.content).append(content.cut($to.parentOffset)));
} else {
var ref = prepareSliceForReplace(slice, $from);
var start = ref.start;
var end = ref.end;
return close(node, replaceThreeWay($from, start, end, $to, depth));
}
}
function checkJoin(main, sub) {
if (!sub.type.compatibleContent(main.type)) {
throw new ReplaceError("Cannot join " + sub.type.name + " onto " + main.type.name);
}
}
function joinable($before, $after, depth) {
var node = $before.node(depth);
checkJoin(node, $after.node(depth));
return node;
}
function addNode(child, target) {
var last = target.length - 1;
if (last >= 0 && child.isText && child.sameMarkup(target[last])) {
target[last] = child.withText(target[last].text + child.text);
} else {
target.push(child);
}
}
function addRange($start, $end, depth, target) {
var node = ($end || $start).node(depth);
var startIndex = 0,
endIndex = $end ? $end.index(depth) : node.childCount;
if ($start) {
startIndex = $start.index(depth);
if ($start.depth > depth) {
startIndex++;
} else if ($start.textOffset) {
addNode($start.nodeAfter, target);
startIndex++;
}
}
for (var i = startIndex; i < endIndex; i++) {
addNode(node.child(i), target);
}
if ($end && $end.depth == depth && $end.textOffset) {
addNode($end.nodeBefore, target);
}
}
function close(node, content) {
if (!node.type.validContent(content, node.attrs)) {
throw new ReplaceError("Invalid content for node " + node.type.name);
}
return node.copy(content);
}
function replaceThreeWay($from, $start, $end, $to, depth) {
var openStart = $from.depth > depth && joinable($from, $start, depth + 1);
var openEnd = $to.depth > depth && joinable($end, $to, depth + 1);
var content = [];
addRange(null, $from, depth, content);
if (openStart && openEnd && $start.index(depth) == $end.index(depth)) {
checkJoin(openStart, openEnd);
addNode(close(openStart, replaceThreeWay($from, $start, $end, $to, depth + 1)), content);
} else {
if (openStart) {
addNode(close(openStart, replaceTwoWay($from, $start, depth + 1)), content);
}
addRange($start, $end, depth, content);
if (openEnd) {
addNode(close(openEnd, replaceTwoWay($end, $to, depth + 1)), content);
}
}
addRange($to, null, depth, content);
return new Fragment(content);
}
function replaceTwoWay($from, $to, depth) {
var content = [];
addRange(null, $from, depth, content);
if ($from.depth > depth) {
var type = joinable($from, $to, depth + 1);
addNode(close(type, replaceTwoWay($from, $to, depth + 1)), content);
}
addRange($to, null, depth, content);
return new Fragment(content);
}
function prepareSliceForReplace(slice, $along) {
var extra = $along.depth - slice.openStart,
parent = $along.node(extra);
var node = parent.copy(slice.content);
for (var i = extra - 1; i >= 0; i--) {
node = $along.node(i).copy(Fragment.from(node));
}
return { start: node.resolveNoCache(slice.openStart + extra),
end: node.resolveNoCache(node.content.size - slice.openEnd - extra) };
}
},{"./fragment":5}],11:[function(require,module,exports){
var ref = require("./mark");
var Mark = ref.Mark;
// ::- You'll often have to '[resolve](#model.Node.resolve)' a
// position to get the context you need. Objects of this class
// represent such a resolved position, providing various pieces of
// context information and helper methods.
//
// Throughout this interface, methods that take an optional `depth`
// parameter will interpret undefined as `this.depth` and negative
// numbers as `this.depth + value`.
var ResolvedPos = function (pos, path, parentOffset) {
// :: number The position that was resolved.
this.pos = pos;
this.path = path;
// :: number
// The number of levels the parent node is from the root. If this
// position points directly into the root, it is 0. If it points
// into a top-level paragraph, 1, and so on.
this.depth = path.length / 3 - 1;
// :: number The offset this position has into its parent node.
this.parentOffset = parentOffset;
};
var prototypeAccessors = { parent: {}, doc: {}, textOffset: {}, nodeAfter: {}, nodeBefore: {} };
ResolvedPos.prototype.resolveDepth = function (val) {
if (val == null) {
return this.depth;
}
if (val < 0) {
return this.depth + val;
}
return val;
};
// :: Node
// The parent node that the position points into. Note that even if
// a position points into a text node, that node is not considered
// the parent—text nodes are 'flat' in this model.
prototypeAccessors.parent.get = function () {
return this.node(this.depth);
};
// :: Node
// The root node in which the position was resolved.
prototypeAccessors.doc.get = function () {
return this.node(0);
};
// :: (?number) → Node
// The ancestor node at the given level. `p.node(p.depth)` is the
// same as `p.parent`.
ResolvedPos.prototype.node = function (depth) {
return this.path[this.resolveDepth(depth) * 3];
};
// :: (?number) → number
// The index into the ancestor at the given level. If this points at
// the 3rd node in the 2nd paragraph on the top level, for example,
// `p.index(0)` is 2 and `p.index(1)` is 3.
ResolvedPos.prototype.index = function (depth) {
return this.path[this.resolveDepth(depth) * 3 + 1];
};
// :: (?number) → number
// The index pointing after this position into the ancestor at the
// given level.
ResolvedPos.prototype.indexAfter = function (depth) {
depth = this.resolveDepth(depth);
return this.index(depth) + (depth == this.depth && !this.textOffset ? 0 : 1);
};
// :: (?number) → number
// The (absolute) position at the start of the node at the given
// level.
ResolvedPos.prototype.start = function (depth) {
depth = this.resolveDepth(depth);
return depth == 0 ? 0 : this.path[depth * 3 - 1] + 1;
};
// :: (?number) → number
// The (absolute) position at the end of the node at the given
// level.
ResolvedPos.prototype.end = function (depth) {
depth = this.resolveDepth(depth);
return this.start(depth) + this.node(depth).content.size;
};
// :: (?number) → number
// The (absolute) position directly before the node at the given
// level, or, when `level` is `this.depth + 1`, the original
// position.
ResolvedPos.prototype.before = function (depth) {
depth = this.resolveDepth(depth);
if (!depth) {
throw new RangeError("There is no position before the top-level node");
}
return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1];
};
// :: (?number) → number
// The (absolute) position directly after the node at the given
// level, or, when `level` is `this.depth + 1`, the original
// position.
ResolvedPos.prototype.after = function (depth) {
depth = this.resolveDepth(depth);
if (!depth) {
throw new RangeError("There is no position after the top-level node");
}
return depth == this.depth + 1 ? this.pos : this.path[depth * 3 - 1] + this.path[depth * 3].nodeSize;
};
// :: number
// When this position points into a text node, this returns the
// distance between the position and the start of the text node.
// Will be zero for positions that point between nodes.
prototypeAccessors.textOffset.get = function () {
return this.pos - this.path[this.path.length - 1];
};
// :: ?Node
// Get the node directly after the position, if any. If the position
// points into a text node, only the part of that node after the
// position is returned.
prototypeAccessors.nodeAfter.get = function () {
var parent = this.parent,
index = this.index(this.depth);
if (index == parent.childCount) {
return null;
}
var dOff = this.pos - this.path[this.path.length - 1],
child = parent.child(index);
return dOff ? parent.child(index).cut(dOff) : child;
};
// :: ?Node
// Get the node directly before the position, if any. If the
// position points into a text node, only the part of that node
// before the position is returned.
prototypeAccessors.nodeBefore.get = function () {
var index = this.index(this.depth);
var dOff = this.pos - this.path[this.path.length - 1];
if (dOff) {
return this.parent.child(index).cut(0, dOff);
}
return index == 0 ? null : this.parent.child(index - 1);
};
// :: (?bool) → [Mark]
// Get the marks at this position, factoring in the surrounding
// marks' [`inclusive`](#model.MarkSpec.inclusive) property. If the
// position is at the start of a non-empty node, or `after` is true,
// the marks of the node after it (if any) are returned.
ResolvedPos.prototype.marks = function (after) {
var parent = this.parent,
index = this.index();
// In an empty parent, return the empty array
if (parent.content.size == 0) {
return Mark.none;
}
// When inside a text node, just return the text node's marks
if (this.textOffset) {
return parent.child(index).marks;
}
var main = parent.maybeChild(index - 1),
other = parent.maybeChild(index);
// If the `after` flag is true of there is no node before, make
// the node after this position the main reference.
if (after && other || !main) {
var tmp = main;main = other;other = tmp;
}
// Use all marks in the main node, except those that have
// `inclusive` set to false and are not present in the other node.
var marks = main.marks;
for (var i = 0; i < marks.length; i++) {
if (marks[i].type.spec.inclusive === false && (!other || !marks[i].isInSet(other.marks))) {
marks = marks[i--].removeFromSet(marks);
}
}
return marks;
};
// :: (number) → number
// The depth up to which this position and the given (non-resolved)
// position share the same parent nodes.
ResolvedPos.prototype.sharedDepth = function (pos) {
var this$1 = this;
for (var depth = this.depth; depth > 0; depth--) {
if (this$1.start(depth) <= pos && this$1.end(depth) >= pos) {
return depth;
}
}
return 0;
};
// :: (?ResolvedPos, ?(Node) → bool) → ?NodeRange
// Returns a range based on the place where this position and the
// given position diverge around block content. If both point into
// the same textblock, for example, a range around that textblock
// will be returned. If they point into different blocks, the range
// around those blocks or their ancestors in their common ancestor
// is returned. You can pass in an optional predicate that will be
// called with a parent node to see if a range into that parent is
// acceptable.
ResolvedPos.prototype.blockRange = function (other, pred) {
var this$1 = this;
if (other === void 0) other = this;
if (other.pos < this.pos) {
return other.blockRange(this);
}
for (var d = this.depth - (this.parent.inlineContent || this.pos == other.pos ? 1 : 0); d >= 0; d--) {
if (other.pos <= this$1.end(d) && (!pred || pred(this$1.node(d)))) {
return new NodeRange(this$1, other, d);
}
}
};
// :: (ResolvedPos) → bool
// Query whether the given position shares the same parent node.
ResolvedPos.prototype.sameParent = function (other) {
return this.pos - this.parentOffset == other.pos - other.parentOffset;
};
// :: (ResolvedPos) → ResolvedPos
// Return the greater of this and the given position.
ResolvedPos.prototype.max = function (other) {
return other.pos > this.pos ? other : this;
};
// :: (ResolvedPos) → ResolvedPos
// Return the smaller of this and the given position.
ResolvedPos.prototype.min = function (other) {
return other.pos < this.pos ? other : this;
};
ResolvedPos.prototype.toString = function () {
var this$1 = this;
var str = "";
for (var i = 1; i <= this.depth; i++) {
str += (str ? "/" : "") + this$1.node(i).type.name + "_" + this$1.index(i - 1);
}
return str + ":" + this.parentOffset;
};
ResolvedPos.resolve = function (doc, pos) {
if (!(pos >= 0 && pos <= doc.content.size)) {
throw new RangeError("Position " + pos + " out of range");
}
var path = [];
var start = 0,
parentOffset = pos;
for (var node = doc;;) {
var ref = node.content.findIndex(parentOffset);
var index = ref.index;
var offset = ref.offset;
var rem = parentOffset - offset;
path.push(node, index, start + offset);
if (!rem) {
break;
}
node = node.child(index);
if (node.isText) {
break;
}
parentOffset = rem - 1;
start += offset + 1;
}
return new ResolvedPos(pos, path, parentOffset);
};
ResolvedPos.resolveCached = function (doc, pos) {
for (var i = 0; i < resolveCache.length; i++) {
var cached = resolveCache[i];
if (cached.pos == pos && cached.node(0) == doc) {
return cached;
}
}
var result = resolveCache[resolveCachePos] = ResolvedPos.resolve(doc, pos);
resolveCachePos = (resolveCachePos + 1) % resolveCacheSize;
return result;
};
Object.defineProperties(ResolvedPos.prototype, prototypeAccessors);
exports.ResolvedPos = ResolvedPos;
var resolveCache = [],
resolveCachePos = 0,
resolveCacheSize = 6;
// ::- Represents a flat range of content.
var NodeRange = function ($from, $to, depth) {
// :: ResolvedPos A resolved position along the start of the
// content. May have a `depth` greater than this object's `depth`
// property, since these are the positions that were used to
// compute the range, not re-resolved positions directly at its
// boundaries.
this.$from = $from;
// :: ResolvedPos A position along the end of the content. See
// caveat for [`$from`](#model.NodeRange.$from).
this.$to = $to;
// :: number The depth of the node that this range points into.
this.depth = depth;
};
var prototypeAccessors$1 = { start: {}, end: {}, parent: {}, startIndex: {}, endIndex: {} };
// :: number The position at the start of the range.
prototypeAccessors$1.start.get = function () {
return this.$from.before(this.depth + 1);
};
// :: number The position at the end of the range.
prototypeAccessors$1.end.get = function () {
return this.$to.after(this.depth + 1);
};
// :: Node The parent node that the range points into.
prototypeAccessors$1.parent.get = function () {
return this.$from.node(this.depth);
};
// :: number The start index of the range in the parent node.
prototypeAccessors$1.startIndex.get = function () {
return this.$from.index(this.depth);
};
// :: number The end index of the range in the parent node.
prototypeAccessors$1.endIndex.get = function () {
return this.$to.indexAfter(this.depth);
};
Object.defineProperties(NodeRange.prototype, prototypeAccessors$1);
exports.NodeRange = NodeRange;
},{"./mark":8}],12:[function(require,module,exports){
var OrderedMap = require("orderedmap");
var ref = require("./node");
var Node = ref.Node;
var TextNode = ref.TextNode;
var ref$1 = require("./fragment");
var Fragment = ref$1.Fragment;
var ref$2 = require("./mark");
var Mark = ref$2.Mark;
var ref$3 = require("./content");
var ContentExpr = ref$3.ContentExpr;
// For node types where all attrs have a default value (or which don't
// have any attributes), build up a single reusable default attribute
// object, and use it for all nodes that don't specify specific
// attributes.
function defaultAttrs(attrs) {
var defaults = Object.create(null);
for (var attrName in attrs) {
var attr = attrs[attrName];
if (attr.default === undefined) {
return null;
}
defaults[attrName] = attr.default;
}
return defaults;
}
function computeAttrs(attrs, value) {
var built = Object.create(null);
for (var name in attrs) {
var given = value && value[name];
if (given == null) {
var attr = attrs[name];
if (attr.default !== undefined) {
given = attr.default;
} else if (attr.compute) {
given = attr.compute();
} else {
throw new RangeError("No value supplied for attribute " + name);
}
}
built[name] = given;
}
return built;
}
function initAttrs(attrs) {
var result = Object.create(null);
if (attrs) {
for (var name in attrs) {
result[name] = new Attribute(attrs[name]);
}
}
return result;
}
// ::- Node types are objects allocated once per `Schema` and used to
// tag `Node` instances with a type. They contain information about
// the node type, such as its name and what kind of node it
// represents.
var NodeType = function (name, schema, spec) {
// :: string
// The name the node type has in this schema.
this.name = name;
// :: Schema
// A link back to the `Schema` the node type belongs to.
this.schema = schema;
// :: NodeSpec
// The spec that this type is based on
this.spec = spec;
this.groups = spec.group ? spec.group.split(" ") : [];
this.attrs = initAttrs(spec.attrs);
this.defaultAttrs = defaultAttrs(this.attrs);
this.contentExpr = null;
// :: bool
// True if this is a block type
this.isBlock = !(spec.inline || name == "text");
// :: bool
// True if this is the text node type.
this.isText = name == "text";
};
var prototypeAccessors = { isInline: {}, isTextblock: {}, inlineContent: {}, isLeaf: {}, isAtom: {} };
// :: bool
// True if this is an inline type.
prototypeAccessors.isInline.get = function () {
return !this.isBlock;
};
// :: bool
// True if this is a textblock type, a block that contains inline
// content.
prototypeAccessors.isTextblock.get = function () {
return this.isBlock && this.contentExpr.inlineContent;
};
// :: bool
// True if this node type has inline content.
prototypeAccessors.inlineContent.get = function () {
return this.contentExpr.inlineContent;
};
// :: bool
// True for node types that allow no content.
prototypeAccessors.isLeaf.get = function () {
return this.contentExpr.isLeaf;
};
// :: bool
// True when this node is an atom, i.e. when it does not have
// directly editable content.
prototypeAccessors.isAtom.get = function () {
return this.isLeaf || this.spec.atom;
};
NodeType.prototype.hasRequiredAttrs = function (ignore) {
var this$1 = this;
for (var n in this$1.attrs) {
if (this$1.attrs[n].isRequired && (!ignore || !(n in ignore))) {
return true;
}
}
return false;
};
NodeType.prototype.compatibleContent = function (other) {
return this == other || this.contentExpr.compatible(other.contentExpr);
};
NodeType.prototype.computeAttrs = function (attrs) {
if (!attrs && this.defaultAttrs) {
return this.defaultAttrs;
} else {
return computeAttrs(this.attrs, attrs);
}
};
// :: (?Object, ?union<Fragment, Node, [Node]>, ?[Mark]) → Node
// Create a `Node` of this type. The given attributes are
// checked and defaulted (you can pass `null` to use the type's
// defaults entirely, if no required attributes exist). `content`
// may be a `Fragment`, a node, an array of nodes, or
// `null`. Similarly `marks` may be `null` to default to the empty
// set of marks.
NodeType.prototype.create = function (attrs, content, marks) {
if (typeof content == "string") {
throw new Error("Calling create with string");
}
if (this.isText) {
throw new Error("NodeType.create can't construct text nodes");
}
return new Node(this, this.computeAttrs(attrs), Fragment.from(content), Mark.setFrom(marks));
};
// :: (?Object, ?union<Fragment, Node, [Node]>, ?[Mark]) → Node
// Like [`create`](#model.NodeType.create), but check the given content
// against the node type's content restrictions, and throw an error
// if it doesn't match.
NodeType.prototype.createChecked = function (attrs, content, marks) {
attrs = this.computeAttrs(attrs);
content = Fragment.from(content);
if (!this.validContent(content, attrs)) {
throw new RangeError("Invalid content for node " + this.name);
}
return new Node(this, attrs, content, Mark.setFrom(marks));
};
// :: (?Object, ?union<Fragment, Node, [Node]>, ?[Mark]) → ?Node
// Like [`create`](#model.NodeType.create), but see if it is necessary to
// add nodes to the start or end of the given fragment to make it
// fit the node. If no fitting wrapping can be found, return null.
// Note that, due to the fact that required nodes can always be
// created, this will always succeed if you pass null or
// `Fragment.empty` as content.
NodeType.prototype.createAndFill = function (attrs, content, marks) {
attrs = this.computeAttrs(attrs);
content = Fragment.from(content);
if (content.size) {
var before = this.contentExpr.start(attrs).fillBefore(content);
if (!before) {
return null;
}
content = before.append(content);
}
var after = this.contentExpr.getMatchAt(attrs, content).fillBefore(Fragment.empty, true);
if (!after) {
return null;
}
return new Node(this, attrs, content.append(after), Mark.setFrom(marks));
};
// :: (Fragment, ?Object) → bool
// Returns true if the given fragment is valid content for this node
// type with the given attributes.
NodeType.prototype.validContent = function (content, attrs) {
return this.contentExpr.matches(attrs, content);
};
NodeType.compile = function (nodes, schema) {
var result = Object.create(null);
nodes.forEach(function (name, spec) {
return result[name] = new NodeType(name, schema, spec);
});
var topType = schema.spec.topNode || "doc";
if (!result[topType]) {
throw new RangeError("Schema is missing its top node type ('" + topType + "')");
}
if (!result.text) {
throw new RangeError("Every schema needs a 'text' type");
}
for (var _ in result.text.attrs) {
throw new RangeError("The text node type should not have attributes");
}
return result;
};
Object.defineProperties(NodeType.prototype, prototypeAccessors);
exports.NodeType = NodeType;
// Attribute descriptors
var Attribute = function (options) {
this.default = options.default;
this.compute = options.compute;
};
var prototypeAccessors$1 = { isRequired: {} };
prototypeAccessors$1.isRequired.get = function () {
return this.default === undefined && !this.compute;
};
Object.defineProperties(Attribute.prototype, prototypeAccessors$1);
// Marks
// ::- Like nodes, marks (which are associated with nodes to signify
// things like emphasis or being part of a link) are tagged with type
// objects, which are instantiated once per `Schema`.
var MarkType = function (name, rank, schema, spec) {
// :: string
// The name of the mark type.
this.name = name;
// :: Schema
// The schema that this mark type instance is part of.
this.schema = schema;
// :: MarkSpec
// The spec on which the type is based.
this.spec = spec;
this.attrs = initAttrs(spec.attrs);
this.rank = rank;
this.excluded = null;
var defaults = defaultAttrs(this.attrs);
this.instance = defaults && new Mark(this, defaults);
};
// :: (?Object) → Mark
// Create a mark of this type. `attrs` may be `null` or an object
// containing only some of the mark's attributes. The others, if
// they have defaults, will be added.
MarkType.prototype.create = function (attrs) {
if (!attrs && this.instance) {
return this.instance;
}
return new Mark(this, computeAttrs(this.attrs, attrs));
};
MarkType.compile = function (marks, schema) {
var result = Object.create(null),
rank = 0;
marks.forEach(function (name, spec) {
return result[name] = new MarkType(name, rank++, schema, spec);
});
return result;
};
// :: ([Mark]) → [Mark]
// When there is a mark of this type in the given set, a new set
// without it is returned. Otherwise, the input set is returned.
MarkType.prototype.removeFromSet = function (set) {
var this$1 = this;
for (var i = 0; i < set.length; i++) {
if (set[i].type == this$1) {
return set.slice(0, i).concat(set.slice(i + 1));
}
}
return set;
};
// :: ([Mark]) → ?Mark
// Tests whether there is a mark of this type in the given set.
MarkType.prototype.isInSet = function (set) {
var this$1 = this;
for (var i = 0; i < set.length; i++) {
if (set[i].type == this$1) {
return set[i];
}
}
};
// :: (MarkType) → bool
// Queries whether a given mark type is
// [excluded](#model.MarkSpec.excludes) by this one.
MarkType.prototype.excludes = function (other) {
return this.excluded.indexOf(other) > -1;
};
exports.MarkType = MarkType;
// SchemaSpec:: interface
// An object describing a schema, as passed to the `Schema`
// constructor.
//
// nodes:: union<Object<NodeSpec>, OrderedMap<NodeSpec>>
// The node types in this schema. Maps names to `NodeSpec` objects
// describing the node to be associated with that name. Their order
// is significant
//
// marks:: ?union<Object<MarkSpec>, OrderedMap<MarkSpec>>
// The mark types that exist in this schema.
//
// topNode:: ?string
// The name of the default top-level node for the schema. Defaults
// to `"doc"`.
// NodeSpec:: interface
//
// content:: ?string
// The content expression for this node, as described in the [schema
// guide](/docs/guides/schema/). When not given, the node does not allow
// any content.
//
// group:: ?string
// The group or space-separated groups to which this node belongs, as
// referred to in the content expressions for the schema.
//
// inline:: ?bool
// Should be set to a truthy value for inline nodes. (Implied for
// text nodes.)
//
// atom:: ?bool
// Can be set to true to indicate that, though this isn't a [leaf
// node](#model.NodeType.isLeaf), it doesn't have directly editable
// content and should be treated as a single unit in the view.
//
// attrs:: ?Object<AttributeSpec>
// The attributes that nodes of this type get.
//
// selectable:: ?bool
// Controls whether nodes of this type can be selected (as a [node
// selection](#state.NodeSelection)). Defaults to true for non-text
// nodes.
//
// draggable:: ?bool
// Determines whether nodes of this type can be dragged without
// being selected. Defaults to false.
//
// code:: ?bool
// Can be used to indicate that this node contains code, which
// causes some commands to behave differently.
//
// defining:: ?bool
// Determines whether this node is considered an important parent
// node during replace operations (such as paste). Non-defining (the
// default) nodes get dropped when their entire content is replaced,
// whereas defining nodes persist and wrap the inserted content.
// Likewise, the the _inserted_ content, when not inserting into a
// textblock, the defining parents of the content are preserved.
// Typically, non-default-paragraph textblock types, and possible
// list items, are marked as defining.
//
// isolating:: ?bool
// When enabled (default is false), the sides of nodes of this type
// count as boundaries that regular editing operations, like
// backspacing or lifting, won't cross. An example of a node that
// should probably have this set is a table cell.
//
// toDOM:: ?(node: Node) → DOMOutputSpec
// Defines the default way a node of this type should be serialized
// to DOM/HTML (as used by
// [`DOMSerializer.fromSchema`](#model.DOMSerializer^fromSchema)).
// Should return an [array structure](#model.DOMOutputSpec) that
// describes the resulting DOM structure, with an optional number
// zero (“hole”) in it to indicate where the node's content should
// be inserted.
//
// For text nodes, the default is to create a text DOM node. Though
// it is possible to create a serializer where text is rendered
// differently, this is not supported inside the editor, so you
// shouldn't override that in your text node spec.
//
// parseDOM:: ?[ParseRule]
// Associates DOM parser information with this node, which can be
// used by [`DOMParser.fromSchema`](#model.DOMParser^fromSchema) to
// automatically derive a parser. The `node` field in the rules is
// implied (the name of this node will be filled in automatically).
// If you supply your own parser, you do not need to also specify
// parsing rules in your schema.
// MarkSpec:: interface
//
// attrs:: ?Object<AttributeSpec>
// The attributes that marks of this type get.
//
// inclusive:: ?bool
// Whether this mark should be active when the cursor is positioned
// at the start or end boundary of the mark. Defaults to true.
//
// excludes:: ?string
// Determines which other marks this mark can coexist with. Should
// be a space-separated strings naming other marks or groups of marks.
// When a mark is [added](#model.mark.addToSet) to a set, all marks
// that it excludes are removed in the process. If the set contains
// any mark that excludes the new mark but is not, itself, excluded
// by the new mark, the mark can not be added an the set. You can
// use the value `"_"` to indicate that the mark excludes all
// marks in the schema.
//
// Defaults to only being exclusive with marks of the same type. You
// can set it to an empty string (or any string not containing the
// mark's own name) to allow multiple marks of a given type to
// coexist (as long as they have different attributes).
//
// group:: ?string
// The group or space-separated groups to which this node belongs.
//
// toDOM:: ?(mark: Mark, inline: bool) → DOMOutputSpec
// Defines the default way marks of this type should be serialized
// to DOM/HTML.
//
// parseDOM:: ?[ParseRule]
// Associates DOM parser information with this mark (see the
// corresponding [node spec field](#model.NodeSpec.parseDOM)). The
// `mark` field in the rules is implied.
// AttributeSpec:: interface
//
// Used to define attributes. Attributes that have no default or
// compute property must be provided whenever a node or mark of a type
// that has them is created.
//
// The following fields are supported:
//
// default:: ?any
// The default value for this attribute, to choose when no
// explicit value is provided.
//
// compute:: ?() → any
// A function that computes a default value for the attribute.
// ::- A document schema.
var Schema = function (spec) {
var this$1 = this;
// :: SchemaSpec
// The [spec](#model.SchemaSpec) on which the schema is based,
// with the added guarantee that its `nodes` and `marks`
// properties are
// [`OrderedMap`](https://github.com/marijnh/orderedmap) instances
// (not raw objects or null).
this.spec = {};
for (var prop in spec) {
this$1.spec[prop] = spec[prop];
}
this.spec.nodes = OrderedMap.from(spec.nodes);
this.spec.marks = OrderedMap.from(spec.marks);
// :: Object<NodeType>
// An object mapping the schema's node names to node type objects.
this.nodes = NodeType.compile(this.spec.nodes, this);
// :: Object<MarkType>
// A map from mark names to mark type objects.
this.marks = MarkType.compile(this.spec.marks, this);
for (var prop$1 in this$1.nodes) {
if (prop$1 in this$1.marks) {
throw new RangeError(prop$1 + " can not be both a node and a mark");
}
var type = this$1.nodes[prop$1];
type.contentExpr = ContentExpr.parse(type, this$1.spec.nodes.get(prop$1).content || "");
}
for (var prop$2 in this$1.marks) {
var type$1 = this$1.marks[prop$2],
excl = type$1.spec.excludes;
type$1.excluded = excl == null ? [type$1] : excl == "" ? [] : ContentExpr.gatherMarks(this$1, excl.split(" "));
}
// :: Object
// An object for storing whatever values modules may want to
// compute and cache per schema. (If you want to store something
// in it, try to use property names unlikely to clash.)
this.cached = Object.create(null);
this.cached.wrappings = Object.create(null);
this.nodeFromJSON = this.nodeFromJSON.bind(this);
this.markFromJSON = this.markFromJSON.bind(this);
// :: NodeType
// The type of the [default top node](#model.SchemaSpec.topNode)
// for this schema.
this.topNodeType = this.nodes[this.spec.topNode || "doc"];
};
// :: (union<string, NodeType>, ?Object, ?union<Fragment, Node, [Node]>, ?[Mark]) → Node
// Create a node in this schema. The `type` may be a string or a
// `NodeType` instance. Attributes will be extended
// with defaults, `content` may be a `Fragment`,
// `null`, a `Node`, or an array of nodes.
Schema.prototype.node = function (type, attrs, content, marks) {
if (typeof type == "string") {
type = this.nodeType(type);
} else if (!(type instanceof NodeType)) {
throw new RangeError("Invalid node type: " + type);
} else if (type.schema != this) {
throw new RangeError("Node type from different schema used (" + type.name + ")");
}
return type.createChecked(attrs, content, marks);
};
// :: (string, ?[Mark]) → Node
// Create a text node in the schema. Empty text nodes are not
// allowed.
Schema.prototype.text = function (text$1, marks) {
var type = this.nodes.text;
return new TextNode(type, type.defaultAttrs, text$1, Mark.setFrom(marks));
};
// :: (union<string, MarkType>, ?Object) → Mark
// Create a mark with the given type and attributes.
Schema.prototype.mark = function (type, attrs) {
if (typeof type == "string") {
type = this.marks[type];
}
return type.create(attrs);
};
// :: (Object) → Node
// Deserialize a node from its JSON representation. This method is
// bound.
Schema.prototype.nodeFromJSON = function (json) {
return Node.fromJSON(this, json);
};
// :: (Object) → Mark
// Deserialize a mark from its JSON representation. This method is
// bound.
Schema.prototype.markFromJSON = function (json) {
return Mark.fromJSON(this, json);
};
Schema.prototype.nodeType = function (name) {
var found = this.nodes[name];
if (!found) {
throw new RangeError("Unknown node type: " + name);
}
return found;
};
exports.Schema = Schema;
},{"./content":3,"./fragment":5,"./mark":8,"./node":9,"orderedmap":1}],13:[function(require,module,exports){
// DOMOutputSpec:: interface
// A description of a DOM structure. Can be either a string, which is
// interpreted as a text node, a DOM node, which is interpreted as
// itself, or an array.
//
// An array describes a DOM element. The first element in the array
// should be a string, and is the name of the DOM element. If the
// second element is a non-Array, non-DOM node object, it is
// interpreted as an object providing the DOM element's attributes.
// Any elements after that (including the 2nd if it's not an attribute
// object) are interpreted as children of the DOM elements, and must
// either be valid `DOMOutputSpec` values, or the number zero.
//
// The number zero (pronounced “hole”) is used to indicate the place
// where a ProseMirror node's content should be inserted.
// ::- A DOM serializer knows how to convert ProseMirror nodes and
// marks of various types to DOM nodes.
var DOMSerializer = function (nodes, marks) {
// :: Object<(node: Node) → DOMOutputSpec>
this.nodes = nodes || {};
// :: Object<(mark: Mark) → DOMOutputSpec>
this.marks = marks || {};
};
// :: (Fragment, ?Object) → dom.DocumentFragment
// Serialize the content of this fragment to a DOM fragment. When
// not in the browser, the `document` option, containing a DOM
// document, should be passed so that the serializer can create
// nodes.
DOMSerializer.prototype.serializeFragment = function (fragment, options, target) {
var this$1 = this;
if (options === void 0) options = {};
if (!target) {
target = doc(options).createDocumentFragment();
}
var top = target,
active = null;
fragment.forEach(function (node) {
if (active || node.marks.length) {
if (!active) {
active = [];
}
var keep = 0;
for (; keep < Math.min(active.length, node.marks.length); ++keep) {
if (!node.marks[keep].eq(active[keep])) {
break;
}
}
while (keep < active.length) {
var removed = active.pop();
if (this$1.marks[removed.type.name]) {
top = top.parentNode;
}
}
while (active.length < node.marks.length) {
var add = node.marks[active.length];
active.push(add);
var markDOM = this$1.serializeMark(add, node.isInline, options);
if (markDOM) {
top = top.appendChild(markDOM);
}
}
}
top.appendChild(this$1.serializeNode(node, options));
});
return target;
};
// :: (Node, ?Object) → dom.Node
// Serialize this node to a DOM node. This can be useful when you
// need to serialize a part of a document, as opposed to the whole
// document. To serialize a whole document, use
// [`serializeFragment`](#model.DOMSerializer.serializeFragment) on
// its [`content`](#model.Node.content).
DOMSerializer.prototype.serializeNode = function (node, options) {
if (options === void 0) options = {};
return this.renderStructure(this.nodes[node.type.name](node), node, options);
};
DOMSerializer.prototype.serializeNodeAndMarks = function (node, options) {
var this$1 = this;
if (options === void 0) options = {};
var dom = this.serializeNode(node, options);
for (var i = node.marks.length - 1; i >= 0; i--) {
var wrap = this$1.serializeMark(node.marks[i], node.isInline, options);
if (wrap) {
wrap.appendChild(dom);
dom = wrap;
}
}
return dom;
};
DOMSerializer.prototype.serializeMark = function (mark, inline, options) {
if (options === void 0) options = {};
var toDOM = this.marks[mark.type.name];
return toDOM && this.renderStructure(toDOM(mark, inline), null, options);
};
// :: (dom.Document, DOMOutputSpec) → {dom: dom.Node, contentDOM: ?dom.Node}
// Render an [output spec](#model.DOMOutputSpec).
DOMSerializer.renderSpec = function (doc, structure) {
if (typeof structure == "string") {
return { dom: doc.createTextNode(structure) };
}
if (structure.nodeType != null) {
return { dom: structure };
}
var dom = doc.createElement(structure[0]),
contentDOM = null;
var attrs = structure[1],
start = 1;
if (attrs && typeof attrs == "object" && attrs.nodeType == null && !Array.isArray(attrs)) {
start = 2;
for (var name in attrs) {
if (name == "style") {
dom.style.cssText = attrs[name];
} else if (attrs[name] != null) {
dom.setAttribute(name, attrs[name]);
}
}
}
for (var i = start; i < structure.length; i++) {
var child = structure[i];
if (child === 0) {
if (i < structure.length - 1 || i > start) {
throw new RangeError("Content hole must be the only child of its parent node");
}
return { dom: dom, contentDOM: dom };
} else {
var ref = DOMSerializer.renderSpec(doc, child);
var inner = ref.dom;
var innerContent = ref.contentDOM;
dom.appendChild(inner);
if (innerContent) {
if (contentDOM) {
throw new RangeError("Multiple content holes");
}
contentDOM = innerContent;
}
}
}
return { dom: dom, contentDOM: contentDOM };
};
DOMSerializer.prototype.renderStructure = function (structure, node, options) {
var ref = DOMSerializer.renderSpec(doc(options), structure);
var dom = ref.dom;
var contentDOM = ref.contentDOM;
if (contentDOM) {
if (!node || node.isLeaf) {
throw new RangeError("Content hole not allowed in a mark or leaf node spec");
}
if (options.onContent) {
options.onContent(node, contentDOM, options);
} else {
this.serializeFragment(node.content, options, contentDOM);
}
}
return dom;
};
// :: (Schema) → DOMSerializer
// Build a serializer using the [`toDOM`](#model.NodeSpec.toDOM)
// properties in a schema's node and mark specs.
DOMSerializer.fromSchema = function (schema) {
return schema.cached.domSerializer || (schema.cached.domSerializer = new DOMSerializer(this.nodesFromSchema(schema), this.marksFromSchema(schema)));
};
// :: (Schema) → Object<(node: Node) → DOMOutputSpec>
// Gather the serializers in a schema's node specs into an object.
// This can be useful as a base to build a custom serializer from.
DOMSerializer.nodesFromSchema = function (schema) {
var result = gatherToDOM(schema.nodes);
if (!result.text) {
result.text = function (node) {
return node.text;
};
}
return result;
};
// :: (Schema) → Object<(mark: Mark) → DOMOutputSpec>
// Gather the serializers in a schema's mark specs into an object.
DOMSerializer.marksFromSchema = function (schema) {
return gatherToDOM(schema.marks);
};
exports.DOMSerializer = DOMSerializer;
function gatherToDOM(obj) {
var result = {};
for (var name in obj) {
var toDOM = obj[name].spec.toDOM;
if (toDOM) {
result[name] = toDOM;
}
}
return result;
}
function doc(options) {
// declare global: window
return options.document || window.document;
}
},{}],14:[function(require,module,exports){
;var assign;
assign = require("./selection"), exports.Selection = assign.Selection, exports.SelectionRange = assign.SelectionRange, exports.TextSelection = assign.TextSelection, exports.NodeSelection = assign.NodeSelection, exports.AllSelection = assign.AllSelection;
exports.Transaction = require("./transaction").Transaction;
exports.EditorState = require("./state").EditorState;var assign$1;
assign$1 = require("./plugin"), exports.Plugin = assign$1.Plugin, exports.PluginKey = assign$1.PluginKey;
},{"./plugin":15,"./selection":16,"./state":17,"./transaction":18}],15:[function(require,module,exports){
// PluginSpec:: interface
//
// A plugin spec provides a definition for a plugin.
//
// props:: ?EditorProps
// The [view props](#view.EditorProps) added by this plugin. Props
// that are functions will be bound to have the plugin instance as
// their `this` binding.
//
// state:: ?StateField<any>
// A [state field](#state.StateField) defined by this plugin.
//
// key:: ?PluginKey
// Can optionally be used to make this a keyed plugin. You can
// have only one plugin with a given key in a given state, but
// it is possible to access the plugin's configuration and state
// through the key, without having access to the plugin instance
// itself.
//
// view:: ?(EditorView) → Object
// When the plugin needs to interact with the editor view, or
// set something up in the DOM, use this field. The function
// will be called when the plugin's state is associated with an
// editor view.
//
// return::-
// Should return an object with the following optional
// properties:
//
// update:: ?(view: EditorView, prevState: EditorState)
// Called whenever the view's state is updated.
//
// destroy:: ?()
// Called when the view is destroyed or receives a state
// with different plugins.
//
// filterTransaction:: ?(Transaction, EditorState) → bool
// When present, this will be called before a transaction is
// applied by the state, allowing the plugin to cancel it (by
// returning false).
//
// appendTransaction:: ?(transactions: [Transaction], oldState: EditorState, newState: EditorState) → ?Transaction
// Allows the plugin to append another transaction to be applied
// after the given array of transactions. When another plugin
// appends a transaction after this was called, it is called
// again with the new state and extended array of transactions.
function bindProps(obj, self, target) {
for (var prop in obj) {
var val = obj[prop];
if (val instanceof Function) {
val = val.bind(self);
} else if (prop == "handleDOMEvents") {
val = bindProps(val, self, {});
}
target[prop] = val;
}
return target;
}
// ::- Plugins wrap extra functionality that can be added to an
// editor. They can define new [state fields](#state.StateField), and
// add [view props](#view.EditorProps).
var Plugin = function Plugin(spec) {
// :: EditorProps
// The props exported by this plugin.
this.props = {};
if (spec.props) {
bindProps(spec.props, this, this.props);
}
// :: Object
// The plugin's configuration object.
this.spec = spec;
this.key = spec.key ? spec.key.key : createKey("plugin");
};
// :: (EditorState) → any
// Get the state field for this plugin.
Plugin.prototype.getState = function getState(state) {
return state[this.key];
};
exports.Plugin = Plugin;
// StateField:: interface<T>
// A plugin may provide a state field (under its `state` property) of
// this type, which describes the state it wants to keep. Functions
// provided here are always called with the plugin instance as their
// `this` binding.
//
// init:: (config: Object, instance: EditorState) → T
// Initialize the value of this field. `config` will be the object
// passed to [`EditorState.create`](#state.EditorState^create). Note
// that `instance` is a half-initialized state instance, and will
// not have values for any fields initialized after this one.
//
// apply:: (tr: Transaction, value: T, oldState: EditorState, newState: EditorState) → T
// Apply the given transaction to this state field, producing a new
// field value. Note that the `newState` argument is a partially
// constructed state does not yet contain the state from plugins
// coming after this plugin.
//
// toJSON:: ?(value: T) → *
// Convert this field to JSON. Optional, can be left off to disable
// JSON serialization for the field.
//
// fromJSON:: ?(config: Object, value: *, state: EditorState) → T
// Deserialize the JSON representation of this field. Note that the
// `state` argument is again a half-initialized state.
var keys = Object.create(null);
function createKey(name) {
if (name in keys) {
return name + "$" + ++keys[name];
}
keys[name] = 0;
return name + "$";
}
// ::- A key is used to [tag](#state.PluginSpec.key)
// plugins in a way that makes it possible to find them, given an
// editor state. Assigning a key does mean only one plugin of that
// type can be active in a state.
var PluginKey = function PluginKey(name) {
if (name === void 0) name = "key";
this.key = createKey(name);
};
// :: (EditorState) → ?Plugin
// Get the active plugin with this key, if any, from an editor
// state.
PluginKey.prototype.get = function get(state) {
return state.config.pluginsByKey[this.key];
};
// :: (EditorState) → ?any
// Get the plugin's state from an editor state.
PluginKey.prototype.getState = function getState(state) {
return state[this.key];
};
exports.PluginKey = PluginKey;
},{}],16:[function(require,module,exports){
var ref = require("prosemirror-model");
var Slice = ref.Slice;
var Fragment = ref.Fragment;
var classesById = Object.create(null);
// ::- Superclass for editor selections. Should not be instantiated
// directly, only extended.
var Selection = function Selection($anchor, $head, ranges) {
// :: [SelectionRange]
// The ranges covered by the selection.
this.ranges = ranges || [new SelectionRange($anchor.min($head), $anchor.max($head))];
// :: ResolvedPos
// The resolved anchor of the selection (the side that stays in
// place when the selection is modified).
this.$anchor = $anchor;
// :: ResolvedPos
// The resolved head of the selection (the side that moves when
// the selection is modified).
this.$head = $head;
};
var prototypeAccessors = { anchor: {}, head: {}, from: {}, to: {}, $from: {}, $to: {}, empty: {} };
// :: number
// The selection's immobile side (does not move when
// shift-selecting).
prototypeAccessors.anchor.get = function () {
return this.$anchor.pos;
};
// :: number
// The selection's mobile side (the side that moves when
// shift-selecting).
prototypeAccessors.head.get = function () {
return this.$head.pos;
};
// :: number
// The lower bound of the selection's first range.
prototypeAccessors.from.get = function () {
return this.$from.pos;
};
// :: number
// The upper bound of the selection's first range.
prototypeAccessors.to.get = function () {
return this.$to.pos;
};
// :: ResolvedPos
// The resolved lowerbound of the selection's main range.
prototypeAccessors.$from.get = function () {
return this.ranges[0].$from;
};
// :: ResolvedPos
// The resolved upper bound of the selection's main range.
prototypeAccessors.$to.get = function () {
return this.ranges[0].$to;
};
// :: bool
// Indicates whether the selection contains any content.
prototypeAccessors.empty.get = function () {
var ranges = this.ranges;
for (var i = 0; i < ranges.length; i++) {
if (ranges[i].$from.pos != ranges[i].$to.pos) {
return false;
}
}
return true;
};
// eq:: (Selection) → bool
// Test whether the selection is the same as another selection.
// map:: (doc: Node, mapping: Mappable) → Selection
// Map this selection through a [mappable](#transform.Mappable) thing. `doc`
// should be the new document, to which we are mapping.
// :: Slice
// Get the content of this selection as a slice.
Selection.prototype.content = function content() {
return this.$from.node(0).slice(this.from, this.to, true);
};
// :: (Transaction, ?Slice)
// Replace the selection with a slice or, if no slice is given,
// delete the selection. Will append to the given transaction.
Selection.prototype.replace = function replace(tr, content) {
if (content === void 0) content = Slice.empty;
// Put the new selection at the position after the inserted
// content. When that ended in an inline node, search backwards,
// to get the position after that node. If not, search forward.
var lastNode = content.content.lastChild,
lastParent = null;
for (var i = 0; i < content.openEnd; i++) {
lastParent = lastNode;
lastNode = lastNode.lastChild;
}
var mapFrom = tr.steps.length,
ranges = this.ranges;
for (var i$1 = 0; i$1 < ranges.length; i$1++) {
var ref = ranges[i$1];
var $from = ref.$from;
var $to = ref.$to;
var mapping = tr.mapping.slice(mapFrom);
tr.replaceRange(mapping.map($from.pos), mapping.map($to.pos), i$1 ? Slice.empty : content);
if (i$1 == 0) {
selectionToInsertionEnd(tr, mapFrom, (lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock) ? -1 : 1);
}
}
};
// :: (Transaction, Node)
// Replace the selection with the given node, appending the changes
// to the given transaction.
Selection.prototype.replaceWith = function replaceWith(tr, node) {
var mapFrom = tr.steps.length,
ranges = this.ranges;
for (var i = 0; i < ranges.length; i++) {
var ref = ranges[i];
var $from = ref.$from;
var $to = ref.$to;
var mapping = tr.mapping.slice(mapFrom);
var from = mapping.map($from.pos),
to = mapping.map($to.pos);
if (i) {
tr.deleteRange(from, to);
} else {
tr.replaceRangeWith(from, to, node);
selectionToInsertionEnd(tr, mapFrom, node.isInline ? -1 : 1);
}
}
};
// toJSON:: () → Object
// Convert the selection to a JSON representation. When implementing
// this for a custom selection class, make sure to give the object a
// `type` property whose value matches the ID under which you
// [registered](#state.Selection^jsonID) your class. The default
// implementation adds `type`, `head`, and `anchor` properties.
// :: (ResolvedPos, number, ?bool) → ?Selection
// Find a valid cursor or leaf node selection starting at the given
// position and searching back if `dir` is negative, and forward if
// negative. When `textOnly` is true, only consider cursor
// selections.
Selection.findFrom = function findFrom($pos, dir, textOnly) {
var inner = $pos.parent.inlineContent ? new TextSelection($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
if (inner) {
return inner;
}
for (var depth = $pos.depth - 1; depth >= 0; depth--) {
var found = dir < 0 ? findSelectionIn($pos.node(0), $pos.node(depth), $pos.before(depth + 1), $pos.index(depth), dir, textOnly) : findSelectionIn($pos.node(0), $pos.node(depth), $pos.after(depth + 1), $pos.index(depth) + 1, dir, textOnly);
if (found) {
return found;
}
}
};
// :: (ResolvedPos, ?number) → Selection
// Find a valid cursor or leaf node selection near the given
// position. Searches forward first by default, but if `bias` is
// negative, it will search backwards first.
Selection.near = function near($pos, bias) {
if (bias === void 0) bias = 1;
return this.findFrom($pos, bias) || this.findFrom($pos, -bias) || new AllSelection($pos.node(0));
};
// :: (Node) → Selection
// Find the cursor or leaf node selection closest to the start of
// the given document. Will return an `AllSelection` if no valid
// position exists.
Selection.atStart = function atStart(doc) {
return findSelectionIn(doc, doc, 0, 0, 1) || new AllSelection(doc);
};
// :: (Node) → Selection
// Find the cursor or leaf node selection closest to the end of the
// given document. Will return an `AllSelection` if no valid
// position exists.
Selection.atEnd = function atEnd(doc) {
return findSelectionIn(doc, doc, doc.content.size, doc.childCount, -1) || new AllSelection(doc);
};
// :: (Node, Object) → Selection
// Deserialize a JSON representation of a selection. Must be
// implemented for custom classes (as a static class method).
Selection.fromJSON = function fromJSON(doc, json) {
var cls = classesById[json.type];
if (!cls) {
return this.backwardsCompatFromJSON(doc, json);
}
return cls.fromJSON(doc, json);
};
Selection.backwardsCompatFromJSON = function backwardsCompatFromJSON(doc, json) {
if (json.anchor != null) {
return TextSelection.fromJSON(doc, json);
}
if (json.node != null) {
return NodeSelection.fromJSON(doc, { anchor: json.node, head: json.after });
}
throw new RangeError("Unrecognized JSON data " + JSON.stringify(json));
};
// :: (string, constructor<Selection>)
// To be able to deserialize selections from JSON, custom selection
// classes must register themselves with an ID string, so that they
// can be disambiguated. Try to pick something that's unlikely to
// clash with classes from other modules.
Selection.jsonID = function jsonID(id, selectionClass) {
if (id in classesById) {
throw new RangeError("Duplicate use of selection JSON ID " + id);
}
classesById[id] = selectionClass;
selectionClass.prototype.jsonID = id;
return selectionClass;
};
// :: () → SelectionBookmark
// Get a [bookmark](#state.SelectionBookmark) for this selection,
// which is a value that can be mapped without having access to a
// current document, and later resolved to a real selection for a
// given document again. (This is used mostly by the history to
// track and restore old selections.) The default implementation of
// this method just converts the selection to a text selection and
// returns the bookmark for that.
Selection.prototype.getBookmark = function getBookmark() {
return TextSelection.between(this.anchor, this.head).getBookmark();
};
Object.defineProperties(Selection.prototype, prototypeAccessors);
exports.Selection = Selection;
// :: bool
// Controls whether, when a selection of this type is active in the
// browser, the selected range should be visible to the user. Defaults
// to `true`.
Selection.prototype.visible = true;
// SelectionBookmark:: interface
// A lightweight, document-independent representation of a selection.
// You can define a custom bookmark type for a custom selection class
// to make the history handle it well.
//
// map:: (mapping: Mapping) → SelectionBookmark
// Map the bookmark through a set of changes.
//
// resolve:: (doc: Node) → Selection
// Resolve the bookmark to a real selection again. This may need to
// do some error checking and may fall back to a default (usually
// [`TextSelection.between`](#state.TextSelection.between) if
// mapping made the bookmark invalid.
// ::- Represents a selected range in a document.
var SelectionRange = function SelectionRange($from, $to) {
// :: ResolvedPos
// The lower bound of the range.
this.$from = $from;
// :: ResolvedPos
// The upper bound of the range.
this.$to = $to;
};
exports.SelectionRange = SelectionRange;
// ::- A text selection represents a classical editor selection, with
// a head (the moving side) and anchor (immobile side), both of which
// point into textblock nodes. It can be empty (a regular cursor
// position).
var TextSelection = function (Selection) {
function TextSelection($anchor, $head) {
if ($head === void 0) $head = $anchor;
Selection.call(this, $anchor, $head);
}
if (Selection) TextSelection.__proto__ = Selection;
TextSelection.prototype = Object.create(Selection && Selection.prototype);
TextSelection.prototype.constructor = TextSelection;
var prototypeAccessors$1 = { $cursor: {} };
// :: ?ResolvedPos
// Returns a resolved position if this is a cursor selection (an
// empty text selection), and null otherwise.
prototypeAccessors$1.$cursor.get = function () {
return this.$anchor.pos == this.$head.pos ? this.$head : null;
};
TextSelection.prototype.map = function map(doc, mapping) {
var $head = doc.resolve(mapping.map(this.head));
if (!$head.parent.inlineContent) {
return Selection.near($head);
}
var $anchor = doc.resolve(mapping.map(this.anchor));
return new TextSelection($anchor.parent.inlineContent ? $anchor : $head, $head);
};
TextSelection.prototype.replace = function replace(tr, content) {
if (content === void 0) content = Slice.empty;
Selection.prototype.replace.call(this, tr, content);
if (content == Slice.empty) {
if (this.$from.parentOffset < this.$from.parent.content.size) {
tr.ensureMarks(this.$from.marks(true));
}
}
};
TextSelection.prototype.eq = function eq(other) {
return other instanceof TextSelection && other.anchor == this.anchor && other.head == this.head;
};
TextSelection.prototype.getBookmark = function getBookmark() {
return new TextBookmark(this.anchor, this.head);
};
TextSelection.prototype.toJSON = function toJSON() {
return { type: "text", anchor: this.anchor, head: this.head };
};
TextSelection.fromJSON = function fromJSON(doc, json) {
return new TextSelection(doc.resolve(json.anchor), doc.resolve(json.head));
};
// :: (Node, number, ?number) → TextSelection
// Create a text selection from non-resolved positions.
TextSelection.create = function create(doc, anchor, head) {
if (head === void 0) head = anchor;
var $anchor = doc.resolve(anchor);
return new this($anchor, head == anchor ? $anchor : doc.resolve(head));
};
// :: (ResolvedPos, ResolvedPos, ?number) → Selection
// Return a text selection that spans the given positions or, if
// they aren't text positions, find a text selection near them.
// `bias` determines whether the method searches forward (default)
// or backwards (negative number) first. Will fall back to calling
// [`Selection.near`](#state.Selection^near) when the document
// doesn't contain a valid text position.
TextSelection.between = function between($anchor, $head, bias) {
var dPos = $anchor.pos - $head.pos;
if (!bias || dPos) {
bias = dPos >= 0 ? 1 : -1;
}
if (!$head.parent.inlineContent) {
var found = Selection.findFrom($head, bias, true) || Selection.findFrom($head, -bias, true);
if (found) {
$head = found.$head;
} else {
return Selection.near($head, bias);
}
}
if (!$anchor.parent.inlineContent) {
if (dPos == 0) {
$anchor = $head;
} else {
$anchor = (Selection.findFrom($anchor, -bias, true) || Selection.findFrom($anchor, bias, true)).$anchor;
if ($anchor.pos < $head.pos != dPos < 0) {
$anchor = $head;
}
}
}
return new TextSelection($anchor, $head);
};
Object.defineProperties(TextSelection.prototype, prototypeAccessors$1);
return TextSelection;
}(Selection);
exports.TextSelection = TextSelection;
Selection.jsonID("text", TextSelection);
var TextBookmark = function TextBookmark(anchor, head) {
this.anchor = anchor;
this.head = head;
};
TextBookmark.prototype.map = function map(mapping) {
return new TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
};
TextBookmark.prototype.resolve = function resolve(doc) {
return TextSelection.between(doc.resolve(this.anchor), doc.resolve(this.head));
};
// ::- A node selection is a selection that points at a
// single node. All nodes marked [selectable](#model.NodeSpec.selectable)
// can be the target of a node selection. In such an object, `from`
// and `to` point directly before and after the selected node.
var NodeSelection = function (Selection) {
function NodeSelection($pos) {
var node = $pos.nodeAfter;
var $end = $pos.node(0).resolve($pos.pos + node.nodeSize);
Selection.call(this, $pos, $end);
// :: Node The selected node.
this.node = node;
}
if (Selection) NodeSelection.__proto__ = Selection;
NodeSelection.prototype = Object.create(Selection && Selection.prototype);
NodeSelection.prototype.constructor = NodeSelection;
NodeSelection.prototype.map = function map(doc, mapping) {
var ref = mapping.mapResult(this.anchor);
var deleted = ref.deleted;
var pos = ref.pos;
var $pos = doc.resolve(pos);
if (deleted) {
return Selection.near($pos);
}
return new NodeSelection($pos);
};
NodeSelection.prototype.content = function content() {
return new Slice(Fragment.from(this.node), 0, 0);
};
NodeSelection.prototype.eq = function eq(other) {
return other instanceof NodeSelection && other.anchor == this.anchor;
};
NodeSelection.prototype.toJSON = function toJSON() {
return { type: "node", anchor: this.anchor };
};
NodeSelection.prototype.getBookmark = function getBookmark() {
return new NodeBookmark(this.anchor);
};
NodeSelection.fromJSON = function fromJSON(doc, json) {
return new NodeSelection(doc.resolve(json.anchor));
};
// :: (Node, number, ?number) → NodeSelection
// Create a node selection from non-resolved positions.
NodeSelection.create = function create(doc, from) {
return new this(doc.resolve(from));
};
// :: (Node) → bool
// Determines whether the given node may be selected as a node
// selection.
NodeSelection.isSelectable = function isSelectable(node) {
return !node.isText && node.type.spec.selectable !== false;
};
return NodeSelection;
}(Selection);
exports.NodeSelection = NodeSelection;
NodeSelection.prototype.visible = false;
Selection.jsonID("node", NodeSelection);
var NodeBookmark = function NodeBookmark(anchor) {
this.anchor = anchor;
};
NodeBookmark.prototype.map = function map(mapping) {
var ref = mapping.mapResult(this.anchor);
var deleted = ref.deleted;
var pos = ref.pos;
return deleted ? new TextBookmark(pos, pos) : new NodeBookmark(pos);
};
NodeBookmark.prototype.resolve = function resolve(doc) {
var $pos = doc.resolve(this.anchor),
node = $pos.nodeAfter;
if (node && NodeSelection.isSelectable(node)) {
return new NodeSelection($pos);
}
return Selection.near($pos);
};
// ::- A selection type that represents selecting the whole document
// (which can not necessarily be expressed with a text selection, when
// there are for example leaf block nodes at the start or end of the
// document).
var AllSelection = function (Selection) {
function AllSelection(doc) {
Selection.call(this, doc.resolve(0), doc.resolve(doc.content.size));
}
if (Selection) AllSelection.__proto__ = Selection;
AllSelection.prototype = Object.create(Selection && Selection.prototype);
AllSelection.prototype.constructor = AllSelection;
AllSelection.prototype.toJSON = function toJSON() {
return { type: "all" };
};
AllSelection.fromJSON = function fromJSON(doc) {
return new AllSelection(doc);
};
AllSelection.prototype.map = function map(doc) {
return new AllSelection(doc);
};
AllSelection.prototype.eq = function eq(other) {
return other instanceof AllSelection;
};
AllSelection.prototype.getBookmark = function getBookmark() {
return AllBookmark;
};
return AllSelection;
}(Selection);
exports.AllSelection = AllSelection;
Selection.jsonID("all", AllSelection);
var AllBookmark = {
map: function map() {
return this;
},
resolve: function resolve(doc) {
return new AllSelection(doc);
}
// FIXME we'll need some awareness of text direction when scanning for selections
// Try to find a selection inside the given node. `pos` points at the
// position where the search starts. When `text` is true, only return
// text selections.
};function findSelectionIn(doc, node, pos, index, dir, text) {
if (node.inlineContent) {
return TextSelection.create(doc, pos);
}
for (var i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
var child = node.child(i);
if (!child.isAtom) {
var inner = findSelectionIn(doc, child, pos + dir, dir < 0 ? child.childCount : 0, dir, text);
if (inner) {
return inner;
}
} else if (!text && NodeSelection.isSelectable(child)) {
return NodeSelection.create(doc, pos - (dir < 0 ? child.nodeSize : 0));
}
pos += child.nodeSize * dir;
}
}
function selectionToInsertionEnd(tr, startLen, bias) {
if (tr.steps.length == startLen) {
return;
}
var map = tr.mapping.maps[tr.mapping.maps.length - 1],
end;
map.forEach(function (_from, _to, _newFrom, newTo) {
return end = newTo;
});
if (end != null) {
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
}
}
},{"prosemirror-model":7}],17:[function(require,module,exports){
var ref = require("prosemirror-model");
var Node = ref.Node;
var ref$1 = require("./selection");
var Selection = ref$1.Selection;
var ref$2 = require("./transaction");
var Transaction = ref$2.Transaction;
function bind(f, self) {
return !self || !f ? f : f.bind(self);
}
var FieldDesc = function FieldDesc(name, desc, self) {
this.name = name;
this.init = bind(desc.init, self);
this.apply = bind(desc.apply, self);
};
var baseFields = [new FieldDesc("doc", {
init: function init(config) {
return config.doc || config.schema.topNodeType.createAndFill();
},
apply: function apply(tr) {
return tr.doc;
}
}), new FieldDesc("selection", {
init: function init(config, instance) {
return config.selection || Selection.atStart(instance.doc);
},
apply: function apply(tr) {
return tr.selection;
}
}), new FieldDesc("storedMarks", {
init: function init() {
return null;
},
apply: function apply(tr, _marks, _old, state) {
return state.selection.$cursor ? tr.storedMarks : null;
}
}), new FieldDesc("scrollToSelection", {
init: function init() {
return 0;
},
apply: function apply(tr, prev) {
return tr.scrolledIntoView ? prev + 1 : prev;
}
})];
// Object wrapping the part of a state object that stays the same
// across transactions. Stored in the state's `config` property.
var Configuration = function Configuration(schema, plugins) {
var this$1 = this;
this.schema = schema;
this.fields = baseFields.concat();
this.plugins = [];
this.pluginsByKey = Object.create(null);
if (plugins) {
plugins.forEach(function (plugin) {
if (this$1.pluginsByKey[plugin.key]) {
throw new RangeError("Adding different instances of a keyed plugin (" + plugin.key + ")");
}
this$1.plugins.push(plugin);
this$1.pluginsByKey[plugin.key] = plugin;
if (plugin.spec.state) {
this$1.fields.push(new FieldDesc(plugin.key, plugin.spec.state, plugin));
}
});
}
};
// ::- The state of a ProseMirror editor is represented by an object
// of this type. This is a persistent data structure—it isn't updated,
// but rather a new state value is computed from an old one with the
// [`apply`](#state.EditorState.apply) method.
//
// In addition to the built-in state fields, plugins can define
// additional pieces of state.
var EditorState = function EditorState(config) {
this.config = config;
};
var prototypeAccessors = { schema: {}, plugins: {}, tr: {} };
// doc:: Node
// The current document.
// selection:: Selection
// The selection.
// storedMarks:: ?[Mark]
// A set of marks to apply to the next character that's typed. Will
// be null whenever no explicit marks have been set.
// :: Schema
// The schema of the state's document.
prototypeAccessors.schema.get = function () {
return this.config.schema;
};
// :: [Plugin]
// The plugins that are active in this state.
prototypeAccessors.plugins.get = function () {
return this.config.plugins;
};
// :: (Transaction) → EditorState
// Apply the given transaction to produce a new state.
EditorState.prototype.apply = function apply(tr) {
return this.applyTransaction(tr).state;
};
// : (Transaction) → ?Transaction
EditorState.prototype.filterTransaction = function filterTransaction(tr, ignore) {
var this$1 = this;
if (ignore === void 0) ignore = -1;
for (var i = 0; i < this.config.plugins.length; i++) {
if (i != ignore) {
var plugin = this$1.config.plugins[i];
if (plugin.spec.filterTransaction && !plugin.spec.filterTransaction.call(plugin, tr, this$1)) {
return false;
}
}
}
return true;
};
// :: (Transaction) → {state: EditorState, transactions: [Transaction]}
// Verbose variant of [`apply`](#state.EditorState.apply) that
// returns the precise transactions that were applied (which might
// be influenced by the [transaction
// hooks](#state.PluginSpec.filterTransaction) of
// plugins) along with the new state.
EditorState.prototype.applyTransaction = function applyTransaction(tr) {
var this$1 = this;
if (!this.filterTransaction(tr)) {
return { state: this, transactions: [] };
}
var trs = [tr],
newState = this.applyInner(tr),
seen = null;
// This loop repeatedly gives plugins a chance to respond to
// transactions as new transactions are added, making sure to only
// pass the transactions the plugin did not see before.
outer: for (;;) {
var haveNew = false;
for (var i = 0; i < this.config.plugins.length; i++) {
var plugin = this$1.config.plugins[i];
if (plugin.spec.appendTransaction) {
var n = seen ? seen[i].n : 0,
oldState = seen ? seen[i].state : this$1;
var tr$1 = n < trs.length && plugin.spec.appendTransaction.call(plugin, n ? trs.slice(n) : trs, oldState, newState);
if (tr$1 && newState.filterTransaction(tr$1, i)) {
tr$1.setMeta("appendedTransaction", tr$1);
if (!seen) {
seen = [];
for (var j = 0; j < this.config.plugins.length; j++) {
seen.push(j < i ? { state: newState, n: trs.length } : { state: this$1, n: 0 });
}
}
trs.push(tr$1);
newState = newState.applyInner(tr$1);
haveNew = true;
}
if (seen) {
seen[i] = { state: newState, n: trs.length };
}
}
}
if (!haveNew) {
return { state: newState, transactions: trs };
}
}
};
// : (Transaction) → EditorState
EditorState.prototype.applyInner = function applyInner(tr) {
var this$1 = this;
if (!tr.before.eq(this.doc)) {
throw new RangeError("Applying a mismatched transaction");
}
var newInstance = new EditorState(this.config),
fields = this.config.fields;
for (var i = 0; i < fields.length; i++) {
var field = fields[i];
newInstance[field.name] = field.apply(tr, this$1[field.name], this$1, newInstance);
}
for (var i$1 = 0; i$1 < applyListeners.length; i$1++) {
applyListeners[i$1](this$1, tr, newInstance);
}
return newInstance;
};
// :: Transaction
// Start a [transaction](#state.Transaction) from this state.
prototypeAccessors.tr.get = function () {
return new Transaction(this);
};
// :: (Object) → EditorState
// Create a state.
//
// config::- Configuration options. Must contain `schema` or `doc` (or both).
//
// schema:: ?Schema
// The schema to use.
//
// doc:: ?Node
// The starting document.
//
// selection:: ?Selection
// A valid selection in the document.
//
// plugins:: ?[Plugin]
// The plugins that should be active in this state.
EditorState.create = function create(config) {
var $config = new Configuration(config.schema || config.doc.type.schema, config.plugins);
var instance = new EditorState($config);
for (var i = 0; i < $config.fields.length; i++) {
instance[$config.fields[i].name] = $config.fields[i].init(config, instance);
}
return instance;
};
// :: (Object) → EditorState
// Create a new state based on this one, but with an adjusted set of
// active plugins. State fields that exist in both sets of plugins
// are kept unchanged. Those that no longer exist are dropped, and
// those that are new are initialized using their
// [`init`](#state.StateField.init) method, passing in the new
// configuration object..
//
// config::- configuration options
//
// schema:: ?Schema
// New schema to use.
//
// plugins:: ?[Plugin]
// New set of active plugins.
EditorState.prototype.reconfigure = function reconfigure(config) {
var this$1 = this;
var $config = new Configuration(config.schema || this.schema, config.plugins);
var fields = $config.fields,
instance = new EditorState($config);
for (var i = 0; i < fields.length; i++) {
var name = fields[i].name;
instance[name] = this$1.hasOwnProperty(name) ? this$1[name] : fields[i].init(config, instance);
}
return instance;
};
// :: (?Object<Plugin>) → Object
// Serialize this state to JSON. If you want to serialize the state
// of plugins, pass an object mapping property names to use in the
// resulting JSON object to plugin objects.
EditorState.prototype.toJSON = function toJSON(pluginFields) {
var this$1 = this;
var result = { doc: this.doc.toJSON(), selection: this.selection.toJSON() };
if (pluginFields) {
for (var prop in pluginFields) {
if (prop == "doc" || prop == "selection") {
throw new RangeError("The JSON fields `doc` and `selection` are reserved");
}
var plugin = pluginFields[prop],
state = plugin.spec.state;
if (state && state.toJSON) {
result[prop] = state.toJSON.call(plugin, this$1[plugin.key]);
}
}
}
return result;
};
// :: (Object, Object, ?Object<Plugin>) → EditorState
// Deserialize a JSON representation of a state. `config` should
// have at least a `schema` field, and should contain array of
// plugins to initialize the state with. `pluginFields` can be used
// to deserialize the state of plugins, by associating plugin
// instances with the property names they use in the JSON object.
//
// config::- configuration options
//
// schema:: Schema
// The schema to use.
//
// plugins:: ?[Plugin]
// The set of active plugins.
EditorState.fromJSON = function fromJSON(config, json, pluginFields) {
if (!config.schema) {
throw new RangeError("Required config field 'schema' missing");
}
var $config = new Configuration(config.schema, config.plugins);
var instance = new EditorState($config);
$config.fields.forEach(function (field) {
if (field.name == "doc") {
instance.doc = Node.fromJSON(config.schema, json.doc);
} else if (field.name == "selection") {
instance.selection = Selection.fromJSON(instance.doc, json.selection);
} else {
if (pluginFields) {
for (var prop in pluginFields) {
var plugin = pluginFields[prop],
state = plugin.spec.state;
if (plugin.key == field.name && state && state.fromJSON && Object.prototype.hasOwnProperty.call(json, prop)) {
// This field belongs to a plugin mapped to a JSON field, read it from there.
instance[field.name] = state.fromJSON.call(plugin, config, json[prop], instance);
return;
}
}
}
instance[field.name] = field.init(config, instance);
}
});
return instance;
};
// Kludge to allow the view to track mappings between different
// instances of a state.
EditorState.addApplyListener = function addApplyListener(f) {
applyListeners.push(f);
};
EditorState.removeApplyListener = function removeApplyListener(f) {
var found = applyListeners.indexOf(f);
if (found > -1) {
applyListeners.splice(found, 1);
}
};
Object.defineProperties(EditorState.prototype, prototypeAccessors);
exports.EditorState = EditorState;
var applyListeners = [];
},{"./selection":16,"./transaction":18,"prosemirror-model":7}],18:[function(require,module,exports){
var ref = require("prosemirror-transform");
var Transform = ref.Transform;
var ref$1 = require("prosemirror-model");
var Mark = ref$1.Mark;
var UPDATED_SEL = 1,
UPDATED_MARKS = 2,
UPDATED_SCROLL = 4;
// ::- An editor state transaction, which can be applied to a state to
// create an updated state. Use
// [`EditorState.tr`](#state.EditorState.tr) to create an instance.
//
// Transactions track changes to the document (they are a subclass of
// [`Transform`](#transform.Transform)), but also other state changes,
// like selection updates and adjustments of the set of [stored
// marks](#state.EditorState.storedMarks). In addition, you can store
// metadata properties in a transaction, which are extra pieces of
// information that client code or plugins can use to describe what a
// transacion represents, so that they can update their [own
// state](#state.StateField) accordingly.
//
// The [editor view](#view.EditorView) uses a single metadata
// property: it will attach a property `"pointer"` with the value
// `true` to selection transactions directly caused by mouse or touch
// input.
var Transaction = function (Transform) {
function Transaction(state) {
Transform.call(this, state.doc);
// :: number
// The timestamp associated with this transaction.
this.time = Date.now();
this.curSelection = state.selection;
// The step count for which the current selection is valid.
this.curSelectionFor = 0;
// :: ?[Mark]
// The stored marks in this transaction.
this.storedMarks = state.storedMarks;
// Bitfield to track which aspects of the state were updated by
// this transaction.
this.updated = 0;
// Object used to store metadata properties for the transaction.
this.meta = Object.create(null);
}
if (Transform) Transaction.__proto__ = Transform;
Transaction.prototype = Object.create(Transform && Transform.prototype);
Transaction.prototype.constructor = Transaction;
var prototypeAccessors = { selection: {}, selectionSet: {}, storedMarksSet: {}, isGeneric: {}, scrolledIntoView: {} };
// :: Selection
// The transform's current selection. This defaults to the
// editor selection [mapped](#state.Selection.map) through the steps in
// this transform, but can be overwritten with
// [`setSelection`](#state.Transaction.setSelection).
prototypeAccessors.selection.get = function () {
if (this.curSelectionFor < this.steps.length) {
this.curSelection = this.curSelection.map(this.doc, this.mapping.slice(this.curSelectionFor));
this.curSelectionFor = this.steps.length;
}
return this.curSelection;
};
// :: (Selection) → Transaction
// Update the transaction's current selection. This will determine
// the selection that the editor gets when the transaction is
// applied.
Transaction.prototype.setSelection = function setSelection(selection) {
this.curSelection = selection;
this.curSelectionFor = this.steps.length;
this.updated = (this.updated | UPDATED_SEL) & ~UPDATED_MARKS;
this.storedMarks = null;
return this;
};
// :: bool
// Whether the selection was explicitly updated by this transaction.
prototypeAccessors.selectionSet.get = function () {
return (this.updated & UPDATED_SEL) > 0;
};
// :: (?[Mark]) → Transaction
// Set the current stored marks.
Transaction.prototype.setStoredMarks = function setStoredMarks(marks) {
this.storedMarks = marks;
this.updated |= UPDATED_MARKS;
return this;
};
// :: ([Mark]) → Transaction
// Make sure the current stored marks or, if that is null, the marks
// at the selection, match the given set of marks. Does nothing if
// this is already the case.
Transaction.prototype.ensureMarks = function ensureMarks(marks) {
if (!Mark.sameSet(this.storedMarks || this.selection.$from.marks(), marks)) {
this.setStoredMarks(marks);
}
return this;
};
// :: bool
// Whether the stored marks were explicitly set for this transaction.
prototypeAccessors.storedMarksSet.get = function () {
return (this.updated & UPDATED_MARKS) > 0;
};
Transaction.prototype.addStep = function addStep(step, doc) {
Transform.prototype.addStep.call(this, step, doc);
this.updated = this.updated & ~UPDATED_MARKS;
this.storedMarks = null;
};
// :: (number) → Transaction
// Update the timestamp for the transaction.
Transaction.prototype.setTime = function setTime(time) {
this.time = time;
return this;
};
// :: (Slice) → Transaction
Transaction.prototype.replaceSelection = function replaceSelection(slice) {
this.selection.replace(this, slice);
return this;
};
// :: (Node, ?bool) → Transaction
// Replace the selection with the given node or slice, or delete it
// if `content` is null. When `inheritMarks` is true and the content
// is inline, it inherits the marks from the place where it is
// inserted.
Transaction.prototype.replaceSelectionWith = function replaceSelectionWith(node, inheritMarks) {
var selection = this.selection;
if (inheritMarks !== false) {
node = node.mark(this.storedMarks || selection.$from.marks(selection.to > selection.from));
}
selection.replaceWith(this, node);
return this;
};
// :: () → Transaction
// Delete the selection.
Transaction.prototype.deleteSelection = function deleteSelection() {
this.selection.replace(this);
return this;
};
// :: (string, from: ?number, to: ?number) → Transaction
// Replace the given range, or the selection if no range is given,
// with a text node containing the given string.
Transaction.prototype.insertText = function insertText(text, from, to) {
if (to === void 0) to = from;
var schema = this.doc.type.schema;
if (from == null) {
if (!text) {
return this.deleteSelection();
}
return this.replaceSelectionWith(schema.text(text), true);
} else {
if (!text) {
return this.deleteRange(from, to);
}
var node = schema.text(text, this.storedMarks || this.doc.resolve(from).marks(to > from));
return this.replaceRangeWith(from, to, node);
}
};
// :: (union<string, Plugin, PluginKey>, any) → Transaction
// Store a metadata property in this transaction, keyed either by
// name or by plugin.
Transaction.prototype.setMeta = function setMeta(key, value) {
this.meta[typeof key == "string" ? key : key.key] = value;
return this;
};
// :: (union<string, Plugin, PluginKey>) → any
// Retrieve a metadata property for a given name or plugin.
Transaction.prototype.getMeta = function getMeta(key) {
return this.meta[typeof key == "string" ? key : key.key];
};
// :: bool
// Returns true if this transaction doesn't contain any metadata,
// and can thus be safely extended.
prototypeAccessors.isGeneric.get = function () {
var this$1 = this;
for (var _ in this$1.meta) {
return false;
}
return true;
};
// :: () → Transaction
// Indicate that the editor should scroll the selection into view
// when updated to the state produced by this transaction.
Transaction.prototype.scrollIntoView = function scrollIntoView() {
this.updated |= UPDATED_SCROLL;
return this;
};
prototypeAccessors.scrolledIntoView.get = function () {
return (this.updated & UPDATED_SCROLL) > 0;
};
// :: (Mark) → Transaction
// Add a mark to the set of stored marks.
Transaction.prototype.addStoredMark = function addStoredMark(mark) {
return this.ensureMarks(mark.addToSet(this.storedMarks || this.selection.$head.marks()));
};
// :: (union<Mark, MarkType>) → Transaction
// Remove a mark or mark type from the set of stored marks.
Transaction.prototype.removeStoredMark = function removeStoredMark(mark) {
return this.ensureMarks(mark.removeFromSet(this.storedMarks || this.selection.$head.marks()));
};
Object.defineProperties(Transaction.prototype, prototypeAccessors);
return Transaction;
}(Transform);
exports.Transaction = Transaction;
},{"prosemirror-model":7,"prosemirror-transform":19}],19:[function(require,module,exports){
;var assign;
assign = require("./transform"), exports.Transform = assign.Transform, exports.TransformError = assign.TransformError;var assign$1;
assign$1 = require("./step"), exports.Step = assign$1.Step, exports.StepResult = assign$1.StepResult;var assign$2;
assign$2 = require("./structure"), exports.joinPoint = assign$2.joinPoint, exports.canJoin = assign$2.canJoin, exports.canSplit = assign$2.canSplit, exports.insertPoint = assign$2.insertPoint, exports.liftTarget = assign$2.liftTarget, exports.findWrapping = assign$2.findWrapping;var assign$3;
assign$3 = require("./map"), exports.StepMap = assign$3.StepMap, exports.MapResult = assign$3.MapResult, exports.Mapping = assign$3.Mapping;var assign$4;
assign$4 = require("./mark_step"), exports.AddMarkStep = assign$4.AddMarkStep, exports.RemoveMarkStep = assign$4.RemoveMarkStep;var assign$5;
assign$5 = require("./replace_step"), exports.ReplaceStep = assign$5.ReplaceStep, exports.ReplaceAroundStep = assign$5.ReplaceAroundStep;
require("./mark");var assign$6;
assign$6 = require("./replace"), exports.replaceStep = assign$6.replaceStep;
},{"./map":20,"./mark":21,"./mark_step":22,"./replace":23,"./replace_step":24,"./step":25,"./structure":26,"./transform":27}],20:[function(require,module,exports){
// Mappable:: interface
// There are several things that positions can be mapped through.
// We'll denote those as 'mappable'.
//
// map:: (pos: number, assoc: ?number) → number
// Map a position through this object. When given, `assoc` (should
// be -1 or 1, defaults to 1) determines with which side the
// 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.
//
// mapResult:: (pos: number, assoc: ?number) → MapResult
// Map a position, and return an object containing additional
// information about the mapping. The result's `deleted` field tells
// you whether the position was deleted (completely enclosed in a
// replaced range) during the mapping.
// 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
// lower 16 bits provide the index, the remaining bits the offset.
//
// Note: We intentionally don't use bit shift operators to en- and
// decode these, since those clip to 32 bits, which we might in rare
// cases want to overflow. A 64-bit float can represent 48-bit
// integers precisely.
var lower16 = 0xffff;
var factor16 = Math.pow(2, 16);
function makeRecover(index, offset) {
return index + offset * factor16;
}
function recoverIndex(value) {
return value & lower16;
}
function recoverOffset(value) {
return (value - (value & lower16)) / factor16;
}
// ::- An object representing a mapped position with extra
// information.
var MapResult = function MapResult(pos, deleted, recover) {
if (deleted === void 0) deleted = false;
if (recover === void 0) recover = null;
// :: number The mapped version of the position.
this.pos = pos;
// :: bool Tells you whether the position was deleted, that is,
// whether the step removed its surroundings from the document.
this.deleted = deleted;
this.recover = recover;
};
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).
var StepMap = function StepMap(ranges, inverted) {
if (inverted === void 0) inverted = false;
this.ranges = ranges;
this.inverted = inverted;
};
StepMap.prototype.recover = function recover(value) {
var this$1 = this;
var diff = 0,
index = recoverIndex(value);
if (!this.inverted) {
for (var i = 0; i < index; i++) {
diff += this$1.ranges[i * 3 + 2] - this$1.ranges[i * 3 + 1];
}
}
return this.ranges[index * 3] + diff + recoverOffset(value);
};
// :: (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.
StepMap.prototype.mapResult = function mapResult(pos, assoc) {
return this._map(pos, assoc, false);
};
// :: (number, ?number) → number
// Map the given position through this map, returning only the
// mapped position.
StepMap.prototype.map = function map(pos, assoc) {
return this._map(pos, assoc, true);
};
StepMap.prototype._map = function _map(pos, assoc, simple) {
var this$1 = this;
var diff = 0,
oldIndex = this.inverted ? 2 : 1,
newIndex = this.inverted ? 1 : 2;
for (var i = 0; i < this.ranges.length; i += 3) {
var start = this$1.ranges[i] - (this$1.inverted ? diff : 0);
if (start > pos) {
break;
}
var oldSize = this$1.ranges[i + oldIndex],
newSize = this$1.ranges[i + newIndex],
end = start + oldSize;
if (pos <= end) {
var side = !oldSize ? assoc : pos == start ? -1 : pos == end ? 1 : assoc;
var result = start + diff + (side < 0 ? 0 : newSize);
if (simple) {
return result;
}
var recover = makeRecover(i / 3, pos - start);
return new MapResult(result, assoc < 0 ? pos != start : pos != end, recover);
}
diff += newSize - oldSize;
}
return simple ? pos + diff : new MapResult(pos + diff);
};
StepMap.prototype.touches = function touches(pos, recover) {
var this$1 = this;
var diff = 0,
index = recoverIndex(recover);
var oldIndex = this.inverted ? 2 : 1,
newIndex = this.inverted ? 1 : 2;
for (var i = 0; i < this.ranges.length; i += 3) {
var start = this$1.ranges[i] - (this$1.inverted ? diff : 0);
if (start > pos) {
break;
}
var oldSize = this$1.ranges[i + oldIndex],
end = start + oldSize;
if (pos <= end && i == index * 3) {
return true;
}
diff += this$1.ranges[i + newIndex] - oldSize;
}
return false;
};
// :: ((oldStart: number, oldEnd: number, newStart: number, newEnd: number))
// Calls the given function on each of the changed ranges denoted by
// this map.
StepMap.prototype.forEach = function forEach(f) {
var this$1 = this;
var oldIndex = this.inverted ? 2 : 1,
newIndex = this.inverted ? 1 : 2;
for (var i = 0, diff = 0; i < this.ranges.length; i += 3) {
var start = this$1.ranges[i],
oldStart = start - (this$1.inverted ? diff : 0),
newStart = start + (this$1.inverted ? 0 : diff);
var oldSize = this$1.ranges[i + oldIndex],
newSize = this$1.ranges[i + newIndex];
f(oldStart, oldStart + oldSize, newStart, newStart + newSize);
diff += newSize - oldSize;
}
};
// :: () → StepMap
// Create an inverted version of this map. The result can be used to
// map positions in the post-step document to the pre-step document.
StepMap.prototype.invert = function invert() {
return new StepMap(this.ranges, !this.inverted);
};
StepMap.prototype.toString = function toString() {
return (this.inverted ? "-" : "") + JSON.stringify(this.ranges);
};
exports.StepMap = StepMap;
StepMap.empty = new StepMap([]);
// ::- 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).
var Mapping = function Mapping(maps, mirror, from, to) {
// :: [StepMap]
// The step maps in this mapping.
this.maps = maps || [];
// :: number
// The starting position in the `maps` array, used when `map` or
// `mapResult` is called.
this.from = from || 0;
// :: number
// The end positions in the `maps` array.
this.to = to == null ? this.maps.length : to;
this.mirror = mirror;
};
// :: (?number, ?number) → Mapping
// Create a mapping that maps only through a part of this one.
Mapping.prototype.slice = function slice(from, to) {
if (from === void 0) from = 0;
if (to === void 0) to = this.maps.length;
return new Mapping(this.maps, this.mirror, from, to);
};
Mapping.prototype.copy = function copy() {
return new Mapping(this.maps.slice(), this.mirror && this.mirror.slice(), this.from, this.to);
};
Mapping.prototype.getMirror = function getMirror(n) {
var this$1 = this;
if (this.mirror) {
for (var i = 0; i < this.mirror.length; i++) {
if (this$1.mirror[i] == n) {
return this$1.mirror[i + (i % 2 ? -1 : 1)];
}
}
}
};
Mapping.prototype.setMirror = function setMirror(n, m) {
if (!this.mirror) {
this.mirror = [];
}
this.mirror.push(n, m);
};
// :: (StepMap, ?number)
// Add a step map to the end of this mapping. If `mirrors` is
// given, it should be the index of the step map that is the mirror
// image of this one.
Mapping.prototype.appendMap = function appendMap(map, mirrors) {
this.to = this.maps.push(map);
if (mirrors != null) {
this.setMirror(this.maps.length - 1, mirrors);
}
};
// :: (Mapping)
// Add all the step maps in a given mapping to this one (preserving
// mirroring information).
Mapping.prototype.appendMapping = function appendMapping(mapping) {
var this$1 = this;
for (var i = 0, startSize = this.maps.length; i < mapping.maps.length; i++) {
var mirr = mapping.getMirror(i);
this$1.appendMap(mapping.maps[i], mirr != null && mirr < i ? startSize + mirr : null);
}
};
// :: (Mapping)
// Append the inverse of the given mapping to this one.
Mapping.prototype.appendMappingInverted = function appendMappingInverted(mapping) {
var this$1 = this;
for (var i = mapping.maps.length - 1, totalSize = this.maps.length + mapping.maps.length; i >= 0; i--) {
var mirr = mapping.getMirror(i);
this$1.appendMap(mapping.maps[i].invert(), mirr != null && mirr > i ? totalSize - mirr - 1 : null);
}
};
// () → Mapping
// Create an inverted version of this mapping.
Mapping.prototype.invert = function invert() {
var inverse = new Mapping();
inverse.appendMappingInverted(this);
return inverse;
};
// :: (number, ?number) → number
// Map a position through this mapping.
Mapping.prototype.map = function map(pos, assoc) {
var this$1 = this;
if (this.mirror) {
return this._map(pos, assoc, true);
}
for (var i = this.from; i < this.to; i++) {
pos = this$1.maps[i].map(pos, assoc);
}
return pos;
};
// :: (number, ?number) → MapResult
// Map a position through this mapping, returning a mapping
// result.
Mapping.prototype.mapResult = function mapResult(pos, assoc) {
return this._map(pos, assoc, false);
};
Mapping.prototype._map = function _map(pos, assoc, simple) {
var this$1 = this;
var deleted = false,
recoverables = null;
for (var i = this.from; i < this.to; i++) {
var map = this$1.maps[i],
rec = recoverables && recoverables[i];
if (rec != null && map.touches(pos, rec)) {
pos = map.recover(rec);
continue;
}
var result = map.mapResult(pos, assoc);
if (result.recover != null) {
var corr = this$1.getMirror(i);
if (corr != null && corr > i && corr < this$1.to) {
if (result.deleted) {
i = corr;
pos = this$1.maps[corr].recover(result.recover);
continue;
} else {
;(recoverables || (recoverables = Object.create(null)))[corr] = result.recover;
}
}
}
if (result.deleted) {
deleted = true;
}
pos = result.pos;
}
return simple ? pos : new MapResult(pos, deleted);
};
exports.Mapping = Mapping;
},{}],21:[function(require,module,exports){
var ref = require("prosemirror-model");
var MarkType = ref.MarkType;
var Slice = ref.Slice;
var Fragment = ref.Fragment;
var ref$1 = require("./transform");
var Transform = ref$1.Transform;
var ref$2 = require("./mark_step");
var AddMarkStep = ref$2.AddMarkStep;
var RemoveMarkStep = ref$2.RemoveMarkStep;
var ref$3 = require("./replace_step");
var ReplaceStep = ref$3.ReplaceStep;
// :: (number, number, Mark) → this
// Add the given mark to the inline content between `from` and `to`.
Transform.prototype.addMark = function (from, to, mark) {
var this$1 = this;
var removed = [],
added = [],
removing = null,
adding = null;
this.doc.nodesBetween(from, to, function (node, pos, parent, index) {
if (!node.isInline) {
return;
}
var marks = node.marks;
if (!mark.isInSet(marks) && parent.contentMatchAt(index).allowsMark(mark.type)) {
var start = Math.max(pos, from),
end = Math.min(pos + node.nodeSize, to);
var newSet = mark.addToSet(marks);
for (var i = 0; i < marks.length; i++) {
if (!marks[i].isInSet(newSet)) {
if (removing && removing.to == start && removing.mark.eq(marks[i])) {
removing.to = end;
} else {
removed.push(removing = new RemoveMarkStep(start, end, marks[i]));
}
}
}
if (adding && adding.to == start) {
adding.to = end;
} else {
added.push(adding = new AddMarkStep(start, end, mark));
}
}
});
removed.forEach(function (s) {
return this$1.step(s);
});
added.forEach(function (s) {
return this$1.step(s);
});
return this;
};
// :: (number, number, ?union<Mark, MarkType>) → this
// Remove the given mark, or all marks of the given type, from inline
// nodes between `from` and `to`.
Transform.prototype.removeMark = function (from, to, mark) {
var this$1 = this;
if (mark === void 0) mark = null;
var matched = [],
step = 0;
this.doc.nodesBetween(from, to, function (node, pos) {
if (!node.isInline) {
return;
}
step++;
var toRemove = null;
if (mark instanceof MarkType) {
var found = mark.isInSet(node.marks);
if (found) {
toRemove = [found];
}
} else if (mark) {
if (mark.isInSet(node.marks)) {
toRemove = [mark];
}
} else {
toRemove = node.marks;
}
if (toRemove && toRemove.length) {
var end = Math.min(pos + node.nodeSize, to);
for (var i = 0; i < toRemove.length; i++) {
var style = toRemove[i],
found$1 = void 0;
for (var j = 0; j < matched.length; j++) {
var m = matched[j];
if (m.step == step - 1 && style.eq(matched[j].style)) {
found$1 = m;
}
}
if (found$1) {
found$1.to = end;
found$1.step = step;
} else {
matched.push({ style: style, from: Math.max(pos, from), to: end, step: step });
}
}
}
});
matched.forEach(function (m) {
return this$1.step(new RemoveMarkStep(m.from, m.to, m.style));
});
return this;
};
// :: (number, number) → this
// Remove all marks and non-text inline nodes from the given range.
Transform.prototype.clearMarkup = function (from, to) {
var this$1 = this;
var delSteps = []; // Must be accumulated and applied in inverse order
this.doc.nodesBetween(from, to, function (node, pos) {
if (!node.isInline) {
return;
}
if (!node.type.isText) {
delSteps.push(new ReplaceStep(pos, pos + node.nodeSize, Slice.empty));
return;
}
for (var i = 0; i < node.marks.length; i++) {
this$1.step(new RemoveMarkStep(Math.max(pos, from), Math.min(pos + node.nodeSize, to), node.marks[i]));
}
});
for (var i = delSteps.length - 1; i >= 0; i--) {
this$1.step(delSteps[i]);
}
return this;
};
Transform.prototype.clearNonMatching = function (pos, match) {
var this$1 = this;
var node = this.doc.nodeAt(pos);
var delSteps = [],
cur = pos + 1;
for (var i = 0; i < node.childCount; i++) {
var child = node.child(i),
end = cur + child.nodeSize;
var allowed = match.matchType(child.type, child.attrs);
if (!allowed) {
delSteps.push(new ReplaceStep(cur, end, Slice.empty));
} else {
match = allowed;
for (var j = 0; j < child.marks.length; j++) {
if (!match.allowsMark(child.marks[j])) {
this$1.step(new RemoveMarkStep(cur, end, child.marks[j]));
}
}
}
cur = end;
}
if (!match.validEnd()) {
var fill = match.fillBefore(Fragment.empty, true);
this.replace(cur, cur, new Slice(fill, 0, 0));
}
for (var i$1 = delSteps.length - 1; i$1 >= 0; i$1--) {
this$1.step(delSteps[i$1]);
}
return this;
};
},{"./mark_step":22,"./replace_step":24,"./transform":27,"prosemirror-model":7}],22:[function(require,module,exports){
var ref = require("prosemirror-model");
var Fragment = ref.Fragment;
var Slice = ref.Slice;
var ref$1 = require("./step");
var Step = ref$1.Step;
var StepResult = ref$1.StepResult;
function mapFragment(fragment, f, parent) {
var mapped = [];
for (var i = 0; i < fragment.childCount; i++) {
var child = fragment.child(i);
if (child.content.size) {
child = child.copy(mapFragment(child.content, f, child));
}
if (child.isInline) {
child = f(child, parent, i);
}
mapped.push(child);
}
return Fragment.fromArray(mapped);
}
// ::- Add a mark to all inline content between two positions.
var AddMarkStep = function (Step) {
function AddMarkStep(from, to, mark) {
Step.call(this);
this.from = from;
this.to = to;
this.mark = mark;
}
if (Step) AddMarkStep.__proto__ = Step;
AddMarkStep.prototype = Object.create(Step && Step.prototype);
AddMarkStep.prototype.constructor = AddMarkStep;
AddMarkStep.prototype.apply = function apply(doc) {
var this$1 = this;
var oldSlice = doc.slice(this.from, this.to),
$from = doc.resolve(this.from);
var parent = $from.node($from.sharedDepth(this.to));
var slice = new Slice(mapFragment(oldSlice.content, function (node, parent, index) {
if (!parent.contentMatchAt(index).allowsMark(this$1.mark.type)) {
return node;
}
return node.mark(this$1.mark.addToSet(node.marks));
}, parent), oldSlice.openStart, oldSlice.openEnd);
return StepResult.fromReplace(doc, this.from, this.to, slice);
};
AddMarkStep.prototype.invert = function invert() {
return new RemoveMarkStep(this.from, this.to, this.mark);
};
AddMarkStep.prototype.map = function map(mapping) {
var from = mapping.mapResult(this.from, 1),
to = mapping.mapResult(this.to, -1);
if (from.deleted && to.deleted || from.pos >= to.pos) {
return null;
}
return new AddMarkStep(from.pos, to.pos, this.mark);
};
AddMarkStep.prototype.merge = function merge(other) {
if (other instanceof AddMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from) {
return new AddMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
}
};
AddMarkStep.prototype.offset = function offset(n) {
return new AddMarkStep(this.from + n, this.to + n, this.mark);
};
AddMarkStep.fromJSON = function fromJSON(schema, json) {
return new AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
};
return AddMarkStep;
}(Step);
exports.AddMarkStep = AddMarkStep;
Step.jsonID("addMark", AddMarkStep);
// ::- Remove a mark from all inline content between two positions.
var RemoveMarkStep = function (Step) {
function RemoveMarkStep(from, to, mark) {
Step.call(this);
this.from = from;
this.to = to;
this.mark = mark;
}
if (Step) RemoveMarkStep.__proto__ = Step;
RemoveMarkStep.prototype = Object.create(Step && Step.prototype);
RemoveMarkStep.prototype.constructor = RemoveMarkStep;
RemoveMarkStep.prototype.apply = function apply(doc) {
var this$1 = this;
var oldSlice = doc.slice(this.from, this.to);
var slice = new Slice(mapFragment(oldSlice.content, function (node) {
return node.mark(this$1.mark.removeFromSet(node.marks));
}), oldSlice.openStart, oldSlice.openEnd);
return StepResult.fromReplace(doc, this.from, this.to, slice);
};
RemoveMarkStep.prototype.invert = function invert() {
return new AddMarkStep(this.from, this.to, this.mark);
};
RemoveMarkStep.prototype.map = function map(mapping) {
var from = mapping.mapResult(this.from, 1),
to = mapping.mapResult(this.to, -1);
if (from.deleted && to.deleted || from.pos >= to.pos) {
return null;
}
return new RemoveMarkStep(from.pos, to.pos, this.mark);
};
RemoveMarkStep.prototype.merge = function merge(other) {
if (other instanceof RemoveMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from) {
return new RemoveMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark);
}
};
RemoveMarkStep.prototype.offset = function offset(n) {
return new RemoveMarkStep(this.from + n, this.to + n, this.mark);
};
RemoveMarkStep.fromJSON = function fromJSON(schema, json) {
return new RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark));
};
return RemoveMarkStep;
}(Step);
exports.RemoveMarkStep = RemoveMarkStep;
Step.jsonID("removeMark", RemoveMarkStep);
},{"./step":25,"prosemirror-model":7}],23:[function(require,module,exports){
var ref = require("prosemirror-model");
var Fragment = ref.Fragment;
var Slice = ref.Slice;
var ref$1 = require("./replace_step");
var ReplaceStep = ref$1.ReplaceStep;
var ReplaceAroundStep = ref$1.ReplaceAroundStep;
var ref$2 = require("./transform");
var Transform = ref$2.Transform;
var ref$3 = require("./structure");
var insertPoint = ref$3.insertPoint;
// :: (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) {
var this$1 = this;
if (!slice.size) {
return this.deleteRange(from, to);
}
var $from = this.doc.resolve(from),
$to = this.doc.resolve(to);
if (fitsTrivially($from, $to, slice)) {
return this.step(new ReplaceStep(from, to, slice));
}
var canExpand = coveredDepths($from, this.doc.resolve(to)),
preferredExpand = 0;
if (canExpand[canExpand.length - 1] == 0) {
canExpand.pop();
}
canExpand.unshift($from.depth + 1);
for (var d = $from.depth; d > 0; d--) {
if ($from.node(d).type.spec.defining) {
break;
}
var found = canExpand.indexOf(d, 1);
if (found > -1) {
preferredExpand = found;
}
}
var leftNodes = [],
preferredDepth = slice.openStart;
for (var content = slice.content, i = 0;; i++) {
var 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 (var j = slice.openStart; j >= 0; j--) {
var openDepth = (j + preferredDepth + 1) % (slice.openStart + 1);
var insert = leftNodes[openDepth];
if (!insert) {
continue;
}
for (var i$1 = 0; i$1 < canExpand.length; i$1++) {
// Loop over possible expansion levels, starting with the
// preferred one
var expandDepth = canExpand[(i$1 + preferredExpand) % canExpand.length];
var parent = $from.node(expandDepth - 1),
index = $from.index(expandDepth - 1);
if (parent.canReplaceWith(index, index, insert.type, insert.attrs, insert.marks)) {
return this$1.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) {
var 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) {
var 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) {
var $from = this.doc.resolve(from),
$to = this.doc.resolve(to);
var covered = coveredDepths($from, $to);
for (var i = 0; i < covered.length; i++) {
var 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) {
var result = [],
minDepth = Math.min($from.depth, $to.depth);
for (var d = minDepth; d >= 0; d--) {
var 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, slice) {
if (to === void 0) to = from;
if (slice === void 0) slice = Slice.empty;
if (from == to && !slice.size) {
return null;
}
var $from = doc.resolve(from),
$to = doc.resolve(to);
// Optimization -- avoid work if it's obvious that it's not needed.
if (fitsTrivially($from, $to, slice)) {
return new ReplaceStep(from, to, slice);
}
var placed = placeSlice($from, slice);
var fittedLeft = fitLeft($from, placed);
var fitted = fitRight($from, $to, fittedLeft);
if (!fitted) {
return null;
}
if (fittedLeft.size != fitted.size && canMoveText($from, $to, fittedLeft)) {
var d = $to.depth,
after = $to.after(d);
while (d > 1 && after == $to.end(--d)) {
++after;
}
var fittedAfter = fitRight($from, doc.resolve(after), fittedLeft);
if (fittedAfter) {
return new ReplaceAroundStep(from, after, to, $to.end(), fittedAfter, fittedLeft.size);
}
}
return new ReplaceStep(from, to, fitted);
}
exports.replaceStep = replaceStep;
// :: (number, ?number, ?Slice) → this
// Replace the part of the document between `from` and `to` with the
// given `slice`.
Transform.prototype.replace = function (from, to, slice) {
if (to === void 0) to = from;
if (slice === void 0) slice = Slice.empty;
var step = replaceStep(this.doc, from, to, slice);
if (step) {
this.step(step);
}
return this;
};
// :: (number, number, union<Fragment, Node, [Node]>) → this
// Replace the given range with the given content, which may be a
// fragment, node, or array of nodes.
Transform.prototype.replaceWith = function (from, to, content) {
return this.replace(from, to, new Slice(Fragment.from(content), 0, 0));
};
// :: (number, union<Fragment, Node, [Node]>) → this
// Insert the given content at the given position.
Transform.prototype.insert = function (pos, content) {
return this.replaceWith(pos, pos, content);
};
function fitLeftInner($from, depth, placed, placedBelow) {
var content = Fragment.empty,
openEnd = 0,
placedHere = placed[depth];
if ($from.depth > depth) {
var inner = fitLeftInner($from, depth + 1, placed, placedBelow || placedHere);
openEnd = inner.openEnd + 1;
content = Fragment.from($from.node(depth + 1).copy(inner.content));
}
if (placedHere) {
content = content.append(placedHere.content);
openEnd = placedHere.openEnd;
}
if (placedBelow) {
content = content.append($from.node(depth).contentMatchAt($from.indexAfter(depth)).fillBefore(Fragment.empty, true));
openEnd = 0;
}
return { content: content, openEnd: openEnd };
}
function fitLeft($from, placed) {
var ref = fitLeftInner($from, 0, placed, false);
var content = ref.content;
var openEnd = ref.openEnd;
return new Slice(content, $from.depth, openEnd || 0);
}
function fitRightJoin(content, parent, $from, $to, depth, openStart, openEnd) {
var match,
count = content.childCount,
matchCount = count - (openEnd > 0 ? 1 : 0);
if (openStart < 0) {
match = parent.contentMatchAt(matchCount);
} else if (count == 1 && openEnd > 0) {
match = $from.node(depth).contentMatchAt(openStart ? $from.index(depth) : $from.indexAfter(depth));
} else {
match = $from.node(depth).contentMatchAt($from.indexAfter(depth)).matchFragment(content, count > 0 && openStart ? 1 : 0, matchCount);
}
var toNode = $to.node(depth);
if (openEnd > 0 && depth < $to.depth) {
var after = toNode.content.cutByIndex($to.indexAfter(depth)).addToStart(content.lastChild);
var joinable$1 = match.fillBefore(after, true);
// Can't insert content if there's a single node stretched across this gap
if (joinable$1 && joinable$1.size && openStart > 0 && count == 1) {
joinable$1 = null;
}
if (joinable$1) {
var inner = fitRightJoin(content.lastChild.content, content.lastChild, $from, $to, depth + 1, count == 1 ? openStart - 1 : -1, openEnd - 1);
if (inner) {
var last = content.lastChild.copy(inner);
if (joinable$1.size) {
return content.cutByIndex(0, count - 1).append(joinable$1).addToEnd(last);
} else {
return content.replaceChild(count - 1, last);
}
}
}
}
if (openEnd > 0) {
match = match.matchNode(count == 1 && openStart > 0 ? $from.node(depth + 1) : content.lastChild);
}
// If we're here, the next level can't be joined, so we see what
// happens if we leave it open.
var toIndex = $to.index(depth);
if (toIndex == toNode.childCount && !toNode.type.compatibleContent(parent.type)) {
return null;
}
var joinable = match.fillBefore(toNode.content, true, toIndex);
if (!joinable) {
return null;
}
if (openEnd > 0) {
var closed = fitRightClosed(content.lastChild, openEnd - 1, $from, depth + 1, count == 1 ? openStart - 1 : -1);
content = content.replaceChild(count - 1, closed);
}
content = content.append(joinable);
if ($to.depth > depth) {
content = content.addToEnd(fitRightSeparate($to, depth + 1));
}
return content;
}
function fitRightClosed(node, openEnd, $from, depth, openStart) {
var match,
content = node.content,
count = content.childCount;
if (openStart >= 0) {
match = $from.node(depth).contentMatchAt($from.indexAfter(depth)).matchFragment(content, openStart > 0 ? 1 : 0, count);
} else {
match = node.contentMatchAt(count);
}
if (openEnd > 0) {
var closed = fitRightClosed(content.lastChild, openEnd - 1, $from, depth + 1, count == 1 ? openStart - 1 : -1);
content = content.replaceChild(count - 1, closed);
}
return node.copy(content.append(match.fillBefore(Fragment.empty, true)));
}
function fitRightSeparate($to, depth) {
var node = $to.node(depth);
var fill = node.contentMatchAt(0).fillBefore(node.content, true, $to.index(depth));
if ($to.depth > depth) {
fill = fill.addToEnd(fitRightSeparate($to, depth + 1));
}
return node.copy(fill);
}
function normalizeSlice(content, openStart, openEnd) {
while (openStart > 0 && openEnd > 0 && content.childCount == 1) {
content = content.firstChild.content;
openStart--;
openEnd--;
}
return new Slice(content, openStart, openEnd);
}
// : (ResolvedPos, ResolvedPos, number, Slice) → Slice
function fitRight($from, $to, slice) {
var fitted = fitRightJoin(slice.content, $from.node(0), $from, $to, 0, slice.openStart, slice.openEnd);
if (!fitted) {
return null;
}
return normalizeSlice(fitted, slice.openStart, $to.depth);
}
function fitsTrivially($from, $to, slice) {
return !slice.openStart && !slice.openEnd && $from.start() == $to.start() && $from.parent.canReplace($from.index(), $to.index(), slice.content);
}
function canMoveText($from, $to, slice) {
if (!$to.parent.isTextblock) {
return false;
}
var match;
if (!slice.openEnd) {
var parent = $from.node($from.depth - (slice.openStart - slice.openEnd));
if (!parent.isTextblock) {
return false;
}
match = parent.contentMatchAt(parent.childCount);
if (slice.size) {
match = match.matchFragment(slice.content, slice.openStart ? 1 : 0);
}
} else {
var parent$1 = nodeRight(slice.content, slice.openEnd);
if (!parent$1.isTextblock) {
return false;
}
match = parent$1.contentMatchAt(parent$1.childCount);
}
match = match.matchFragment($to.parent.content, $to.index());
return match && match.validEnd();
}
// Algorithm for 'placing' the elements of a slice into a gap:
//
// We consider the content of each node that is open to the left to be
// independently placeable. I.e. in <p("foo"), p("bar")>, when the
// paragraph on the left is open, "foo" can be placed (somewhere on
// the left side of the replacement gap) independently from p("bar").
//
// So placeSlice splits up a slice into a number of sub-slices,
// along with information on where they can be placed on the given
// left-side edge. It works by walking the open side of the slice,
// from the inside out, and trying to find a landing spot for each
// element, by simultaneously scanning over the gap side. When no
// place is found for an open node's content, it is left in that node.
//
// If the outer content can't be placed, a set of wrapper nodes is
// made up for it (by rooting it in the document node type using
// findWrapping), and the algorithm continues to iterate over those.
// This is guaranteed to find a fit, since both stacks now start with
// the same node type (doc).
function nodeLeft(content, depth) {
for (var i = 1; i < depth; i++) {
content = content.firstChild.content;
}
return content.firstChild;
}
function nodeRight(content, depth) {
for (var i = 1; i < depth; i++) {
content = content.lastChild.content;
}
return content.lastChild;
}
// : (ResolvedPos, Slice) → [{content: Fragment, openEnd: number, depth: number}]
function placeSlice($from, slice) {
var dFrom = $from.depth,
unplaced = null;
var placed = [],
parents = null;
// Loop over the open side of the slice, trying to find a place for
// each open fragment.
for (var dSlice = slice.openStart;; --dSlice) {
// Get the components of the node at this level
var curType = void 0,
curAttrs = void 0,
curFragment = void 0;
if (dSlice >= 0) {
if (dSlice > 0) {
// Inside slice
;var assign;
assign = nodeLeft(slice.content, dSlice), curType = assign.type, curAttrs = assign.attrs, curFragment = assign.content;
} else if (dSlice == 0) {
// Top of slice
curFragment = slice.content;
}
if (dSlice < slice.openStart) {
curFragment = curFragment.cut(curFragment.firstChild.nodeSize);
}
} else {
// Outside slice, in generated wrappers (see below)
curFragment = Fragment.empty;
var parent = parents[parents.length + dSlice - 1];
curType = parent.type;
curAttrs = parent.attrs;
}
// If the last iteration left unplaced content, include it in the fragment
if (unplaced) {
curFragment = curFragment.addToStart(unplaced);
}
// If there's nothing left to place, we're done
if (curFragment.size == 0 && dSlice <= 0) {
break;
}
// This will go through the positions in $from, down from dFrom,
// to find a fit
var found = findPlacement(curFragment, $from, dFrom, placed);
if (found && unneccesaryFallthrough($from, dFrom, found.depth, slice, dSlice)) {
found = null;
}
if (found) {
// 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 that was the last of the content, we're done
if (dSlice <= 0) {
break;
}
unplaced = null;
dFrom = found.depth - (curType == $from.node(found.depth).type ? 1 : 0);
} else {
if (dSlice == 0) {
// This is the top of the slice, and we haven't found a place to insert it.
var top = $from.node(0);
// Try to find a wrapping that makes its first child fit in the top node.
var wrap = top.contentMatchAt($from.index(0)).findWrappingFor(curFragment.firstChild);
// If no such thing exists, give up.
if (!wrap || wrap.length == 0) {
break;
}
var last = wrap[wrap.length - 1];
// Check that the fragment actually fits in the wrapping.
if (!last.type.contentExpr.matches(last.attrs, curFragment)) {
break;
}
// Store the result for subsequent iterations.
parents = [{ type: top.type, attrs: top.attrs }].concat(wrap);var assign$1;
assign$1 = last, curType = assign$1.type, curAttrs = assign$1.attrs;
}
if (curFragment.size) {
curFragment = curType.contentExpr.start(curAttrs).fillBefore(curFragment, true).append(curFragment);
unplaced = curType.create(curAttrs, curFragment);
} else {
unplaced = null;
}
}
}
return placed;
}
function endOfContent(slice, depth) {
for (var i = 0, content = slice.content; i < depth; i++) {
if (content.childCount > 1) {
return false;
}
content = content.firstChild.content;
}
return true;
}
function findPlacement(fragment, $from, start, placed) {
var hasMarks = false;
for (var i = 0; i < fragment.childCount; i++) {
if (fragment.child(i).marks.length) {
hasMarks = true;
}
}
for (var d = start; d >= 0; d--) {
var startMatch = $from.node(d).contentMatchAt($from.indexAfter(d));
var existing = placed[d];
if (existing) {
startMatch = startMatch.matchFragment(existing.content);
}
var match = startMatch.fillBefore(fragment);
if (match) {
return { depth: d, fragment: (existing ? existing.content.append(match) : match).append(fragment) };
}
if (hasMarks) {
var stripped = matchStrippingMarks(startMatch, fragment);
if (stripped) {
return { depth: d, fragment: existing ? existing.content.append(stripped) : stripped };
}
}
}
}
function matchStrippingMarks(match, fragment) {
var newNodes = [];
for (var i = 0; i < fragment.childCount; i++) {
var node = fragment.child(i),
stripped = node.mark(node.marks.filter(function (m) {
return match.allowsMark(m.type);
}));
match = match.matchNode(stripped);
if (!match) {
return null;
}
newNodes.push(stripped);
}
return Fragment.from(newNodes);
}
function unneccesaryFallthrough($from, dFrom, dFound, slice, dSlice) {
if (dSlice < 1) {
return false;
}
for (; dFrom > dFound; dFrom--) {
var here = $from.node(dFrom).contentMatchAt($from.indexAfter(dFrom));
for (var d = dSlice - 1; d >= 0; d--) {
if (here.matchNode(nodeLeft(slice.content, d))) {
return true;
}
}
}
return false;
}
},{"./replace_step":24,"./structure":26,"./transform":27,"prosemirror-model":7}],24:[function(require,module,exports){
var ref = require("prosemirror-model");
var Slice = ref.Slice;
var ref$1 = require("./step");
var Step = ref$1.Step;
var StepResult = ref$1.StepResult;
var ref$2 = require("./map");
var StepMap = ref$2.StepMap;
// ::- Replace a part of the document with a slice of new content.
var ReplaceStep = function (Step) {
function ReplaceStep(from, to, slice, structure) {
Step.call(this);
this.from = from;
this.to = to;
this.slice = slice;
this.structure = !!structure;
}
if (Step) ReplaceStep.__proto__ = Step;
ReplaceStep.prototype = Object.create(Step && Step.prototype);
ReplaceStep.prototype.constructor = ReplaceStep;
ReplaceStep.prototype.apply = function apply(doc) {
if (this.structure && contentBetween(doc, this.from, this.to)) {
return StepResult.fail("Structure replace would overwrite content");
}
return StepResult.fromReplace(doc, this.from, this.to, this.slice);
};
ReplaceStep.prototype.getMap = function getMap() {
return new StepMap([this.from, this.to - this.from, this.slice.size]);
};
ReplaceStep.prototype.invert = function invert(doc) {
return new ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to));
};
ReplaceStep.prototype.map = function map(mapping) {
var from = mapping.mapResult(this.from, 1),
to = mapping.mapResult(this.to, -1);
if (from.deleted && to.deleted) {
return null;
}
return new ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice);
};
ReplaceStep.prototype.merge = function merge(other) {
if (!(other instanceof ReplaceStep) || other.structure != this.structure) {
return null;
}
if (this.from + this.slice.size == other.from && !this.slice.openEnd && !other.slice.openStart) {
var slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(this.slice.content.append(other.slice.content), this.slice.openStart, other.slice.openEnd);
return new ReplaceStep(this.from, this.to + (other.to - other.from), slice, this.structure);
} else if (other.to == this.from && !this.slice.openStart && !other.slice.openEnd) {
var slice$1 = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(other.slice.content.append(this.slice.content), other.slice.openStart, this.slice.openEnd);
return new ReplaceStep(other.from, this.to, slice$1, this.structure);
} else {
return null;
}
};
ReplaceStep.prototype.toJSON = function toJSON() {
var json = { stepType: "replace", from: this.from, to: this.to };
if (this.slice.size) {
json.slice = this.slice.toJSON();
}
if (this.structure) {
json.structure = true;
}
return json;
};
ReplaceStep.prototype.offset = function offset(n) {
return new ReplaceStep(this.from + n, this.to + n, this.slice, this.structure);
};
ReplaceStep.fromJSON = function fromJSON(schema, json) {
return new ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure);
};
return ReplaceStep;
}(Step);
exports.ReplaceStep = ReplaceStep;
Step.jsonID("replace", ReplaceStep);
// ::- Replace a part of the document with a slice of content, but
// preserve a range of the replaced content by moving it into the
// slice.
var ReplaceAroundStep = function (Step) {
function ReplaceAroundStep(from, to, gapFrom, gapTo, slice, insert, structure) {
Step.call(this);
this.from = from;
this.to = to;
this.gapFrom = gapFrom;
this.gapTo = gapTo;
this.slice = slice;
this.insert = insert;
this.structure = !!structure;
}
if (Step) ReplaceAroundStep.__proto__ = Step;
ReplaceAroundStep.prototype = Object.create(Step && Step.prototype);
ReplaceAroundStep.prototype.constructor = ReplaceAroundStep;
ReplaceAroundStep.prototype.apply = function apply(doc) {
if (this.structure && (contentBetween(doc, this.from, this.gapFrom) || contentBetween(doc, this.gapTo, this.to))) {
return StepResult.fail("Structure gap-replace would overwrite content");
}
var gap = doc.slice(this.gapFrom, this.gapTo);
if (gap.openStart || gap.openEnd) {
return StepResult.fail("Gap is not a flat range");
}
var inserted = this.slice.insertAt(this.insert, gap.content);
if (!inserted) {
return StepResult.fail("Content does not fit in gap");
}
return StepResult.fromReplace(doc, this.from, this.to, inserted);
};
ReplaceAroundStep.prototype.getMap = function getMap() {
return new StepMap([this.from, this.gapFrom - this.from, this.insert, this.gapTo, this.to - this.gapTo, this.slice.size - this.insert]);
};
ReplaceAroundStep.prototype.invert = function invert(doc) {
var gap = this.gapTo - this.gapFrom;
return new ReplaceAroundStep(this.from, this.from + this.slice.size + gap, this.from + this.insert, this.from + this.insert + gap, doc.slice(this.from, this.to).removeBetween(this.gapFrom - this.from, this.gapTo - this.from), this.gapFrom - this.from, this.structure);
};
ReplaceAroundStep.prototype.map = function map(mapping) {
var from = mapping.mapResult(this.from, 1),
to = mapping.mapResult(this.to, -1);
var gapFrom = mapping.map(this.gapFrom, -1),
gapTo = mapping.map(this.gapTo, 1);
if (from.deleted && to.deleted || gapFrom < from.pos || gapTo > to.pos) {
return null;
}
return new ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure);
};
ReplaceAroundStep.toJSON = function toJSON() {
var json = { stepType: "replaceAround", from: this.from, to: this.to,
gapFrom: this.gapFrom, gapTo: this.gapTo, slice: this.slice.toJSON() };
if (this.structure) {
json.structure = true;
}
return true;
};
ReplaceAroundStep.prototype.offset = function offset(n) {
return new ReplaceAroundStep(this.from + n, this.to + n, this.gapFrom + n, this.gapTo + n, this.slice, this.insert, this.structure);
};
ReplaceAroundStep.fromJSON = function fromJSON(schema, json) {
return new ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo, Slice.fromJSON(schema, json.slice), json.insert, !!json.structure);
};
return ReplaceAroundStep;
}(Step);
exports.ReplaceAroundStep = ReplaceAroundStep;
Step.jsonID("replaceAround", ReplaceAroundStep);
function contentBetween(doc, from, to) {
var $from = doc.resolve(from),
dist = to - from,
depth = $from.depth;
while (dist > 0 && depth > 0 && $from.indexAfter(depth) == $from.node(depth).childCount) {
depth--;
dist--;
}
if (dist > 0) {
var next = $from.node(depth).maybeChild($from.indexAfter(depth));
while (dist > 0) {
if (!next || next.isLeaf) {
return true;
}
next = next.firstChild;
dist--;
}
}
return false;
}
},{"./map":20,"./step":25,"prosemirror-model":7}],25:[function(require,module,exports){
var ref = require("prosemirror-model");
var ReplaceError = ref.ReplaceError;
var ref$1 = require("./map");
var StepMap = ref$1.StepMap;
function mustOverride() {
throw new Error("Override me");
}
var stepsByID = Object.create(null);
// ::- A step object wraps an atomic operation. It generally applies
// only to the document it was created for, since the positions
// associated with it will only make sense for that document.
//
// New steps are defined by creating classes that extend `Step`,
// overriding the `apply`, `invert`, `map`, `getMap` and `fromJSON`
// methods, and registering your class with a unique
// JSON-serialization identifier using
// [`Step.jsonID`](#transform.Step^jsonID).
var Step = function Step() {};
Step.prototype.apply = function apply(_doc) {
return mustOverride();
};
// :: () → StepMap
// Get the step map that represents the changes made by this
// step.
Step.prototype.getMap = function getMap() {
return StepMap.empty;
};
// :: (doc: Node) → Step
// Create an inverted version of this step. Needs the document as it
// was before the step as argument.
Step.prototype.invert = function invert(_doc) {
return mustOverride();
};
// :: (mapping: Mappable) → ?Step
// Map this step through a mappable thing, returning either a
// version of that step with its positions adjusted, or `null` if
// the step was entirely deleted by the mapping.
Step.prototype.map = function map(_mapping) {
return mustOverride();
};
// :: (other: Step) → ?Step
// Try to merge this step with another one, to be applied directly
// after it. Returns the merged step when possible, null if the
// steps can't be merged.
Step.prototype.merge = function merge(_other) {
return null;
};
// :: (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.
Step.prototype.offset = function 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.
Step.prototype.toJSON = function toJSON() {
var this$1 = this;
var obj = { stepType: this.jsonID };
for (var prop in this$1) {
if (this$1.hasOwnProperty(prop)) {
var val = this$1[prop];
obj[prop] = val && val.toJSON ? val.toJSON() : val;
}
}
return obj;
};
// :: (Schema, Object) → Step
// Deserialize a step from its JSON representation. Will call
// through to the step class' own implementation of this method.
Step.fromJSON = function fromJSON(schema, json) {
return stepsByID[json.stepType].fromJSON(schema, json);
};
// :: (string, constructor<Step>)
// To be able to serialize steps to JSON, each step needs a string
// ID to attach to its JSON representation. Use this method to
// register an ID for your step classes. Try to pick something
// that's unlikely to clash with steps from other modules.
Step.jsonID = function jsonID(id, stepClass) {
if (id in stepsByID) {
throw new RangeError("Duplicate use of step JSON ID " + id);
}
stepsByID[id] = stepClass;
stepClass.prototype.jsonID = id;
return stepClass;
};
exports.Step = Step;
// ::- The result of [applying](#transform.Step.apply) a step. Contains either a
// new document or a failure value.
var StepResult = function StepResult(doc, failed) {
// :: ?Node The transformed document.
this.doc = doc;
// :: ?string Text providing information about a failed step.
this.failed = failed;
};
// :: (Node) → StepResult
// Create a successful step result.
StepResult.ok = function ok(doc) {
return new StepResult(doc, null);
};
// :: (string) → StepResult
// Create a failed step result.
StepResult.fail = function fail(message) {
return new StepResult(null, message);
};
// :: (Node, number, number, Slice) → StepResult
// Call [`Node.replace`](#model.Node.replace) with the given
// arguments. Create a successful result if it succeeds, and a
// failed one if it throws a `ReplaceError`.
StepResult.fromReplace = function fromReplace(doc, from, to, slice) {
try {
return StepResult.ok(doc.replace(from, to, slice));
} catch (e) {
if (e instanceof ReplaceError) {
return StepResult.fail(e.message);
}
throw e;
}
};
exports.StepResult = StepResult;
},{"./map":20,"prosemirror-model":7}],26:[function(require,module,exports){
var ref = require("prosemirror-model");
var Slice = ref.Slice;
var Fragment = ref.Fragment;
var ref$1 = require("./transform");
var Transform = ref$1.Transform;
var ref$2 = require("./replace_step");
var ReplaceStep = ref$2.ReplaceStep;
var ReplaceAroundStep = ref$2.ReplaceAroundStep;
function canCut(node, start, end) {
return (start == 0 || node.canReplace(start, node.childCount)) && (end == node.childCount || node.canReplace(0, end));
}
// :: (NodeRange) → ?number
// Try to find a target depth to which the content in the given range
// can be lifted. Will not go across
// [isolating](#model.NodeSpec.isolating) parent nodes.
function liftTarget(range) {
var parent = range.parent;
var content = parent.content.cutByIndex(range.startIndex, range.endIndex);
for (var depth = range.depth;; --depth) {
var node = range.$from.node(depth);
var index = range.$from.index(depth),
endIndex = range.$to.indexAfter(depth);
if (depth < range.depth && node.canReplace(index, endIndex, content)) {
return depth;
}
if (depth == 0 || node.type.spec.isolating || !canCut(node, index, endIndex)) {
break;
}
}
}
exports.liftTarget = liftTarget;
// :: (NodeRange, number) → this
// Split the content in the given range off from its parent, if there
// is sibling content before or after it, and move it up the tree to
// the depth specified by `target`. You'll probably want to use
// `liftTarget` to compute `target`, in order to be sure the lift is
// valid.
Transform.prototype.lift = function (range, target) {
var $from = range.$from;
var $to = range.$to;
var depth = range.depth;
var gapStart = $from.before(depth + 1),
gapEnd = $to.after(depth + 1);
var start = gapStart,
end = gapEnd;
var before = Fragment.empty,
openStart = 0;
for (var d = depth, splitting = false; d > target; d--) {
if (splitting || $from.index(d) > 0) {
splitting = true;
before = Fragment.from($from.node(d).copy(before));
openStart++;
} else {
start--;
}
}
var after = Fragment.empty,
openEnd = 0;
for (var d$1 = depth, splitting$1 = false; d$1 > target; d$1--) {
if (splitting$1 || $to.after(d$1 + 1) < $to.end(d$1)) {
splitting$1 = true;
after = Fragment.from($to.node(d$1).copy(after));
openEnd++;
} else {
end++;
}
}
return this.step(new ReplaceAroundStep(start, end, gapStart, gapEnd, new Slice(before.append(after), openStart, openEnd), before.size - openStart, true));
};
// :: (NodeRange, NodeType, ?Object) → ?[{type: NodeType, attrs: ?Object}]
// Try to find a valid way to wrap the content in the given range in a
// node of the given type. May introduce extra nodes around and inside
// the wrapper node, if necessary. Returns null if no valid wrapping
// could be found.
function findWrapping(range, nodeType, attrs, innerRange) {
if (innerRange === void 0) innerRange = range;
var wrap = { type: nodeType, attrs: attrs };
var around = findWrappingOutside(range, wrap);
var inner = around && findWrappingInside(innerRange, wrap);
if (!inner) {
return null;
}
return around.concat(wrap).concat(inner);
}
exports.findWrapping = findWrapping;
function findWrappingOutside(range, wrap) {
var parent = range.parent;
var startIndex = range.startIndex;
var endIndex = range.endIndex;
var around = parent.contentMatchAt(startIndex).findWrapping(wrap.type, wrap.attrs);
if (!around) {
return null;
}
var outer = around.length ? around[0] : wrap;
if (!parent.canReplaceWith(startIndex, endIndex, outer.type, outer.attrs)) {
return null;
}
return around;
}
function findWrappingInside(range, wrap) {
var parent = range.parent;
var startIndex = range.startIndex;
var endIndex = range.endIndex;
var inner = parent.child(startIndex);
var inside = wrap.type.contentExpr.start(wrap.attrs).findWrappingFor(inner);
if (!inside) {
return null;
}
var last = inside.length ? inside[inside.length - 1] : wrap;
var innerMatch = last.type.contentExpr.start(last.attrs);
for (var i = startIndex; i < endIndex; i++) {
innerMatch = innerMatch && innerMatch.matchNode(parent.child(i));
}
if (!innerMatch || !innerMatch.validEnd()) {
return null;
}
return inside;
}
// :: (NodeRange, [{type: NodeType, attrs: ?Object}]) → this
// Wrap the given [range](#model.NodeRange) in the given set of wrappers.
// The wrappers are assumed to be valid in this position, and should
// probably be computed with `findWrapping`.
Transform.prototype.wrap = function (range, wrappers) {
var content = Fragment.empty;
for (var i = wrappers.length - 1; i >= 0; i--) {
content = Fragment.from(wrappers[i].type.create(wrappers[i].attrs, content));
}
var start = range.start,
end = range.end;
return this.step(new ReplaceAroundStep(start, end, start, end, new Slice(content, 0, 0), wrappers.length, true));
};
// :: (number, ?number, NodeType, ?Object) → this
// Set the type of all textblocks (partly) between `from` and `to` to
// the given node type with the given attributes.
Transform.prototype.setBlockType = function (from, to, type, attrs) {
var this$1 = this;
if (to === void 0) to = from;
if (!type.isTextblock) {
throw new RangeError("Type given to setBlockType should be a textblock");
}
var mapFrom = this.steps.length;
this.doc.nodesBetween(from, to, function (node, pos) {
if (node.isTextblock && !node.hasMarkup(type, attrs)) {
// Ensure all markup that isn't allowed in the new node type is cleared
this$1.clearNonMatching(this$1.mapping.slice(mapFrom).map(pos, 1), type.contentExpr.start(attrs));
var mapping = this$1.mapping.slice(mapFrom);
var startM = mapping.map(pos, 1),
endM = mapping.map(pos + node.nodeSize, 1);
this$1.step(new ReplaceAroundStep(startM, endM, startM + 1, endM - 1, new Slice(Fragment.from(type.create(attrs)), 0, 0), 1, true));
return false;
}
});
return this;
};
// :: (number, ?NodeType, ?Object, ?[Mark]) → this
// Change the type and attributes of the node after `pos`.
Transform.prototype.setNodeType = function (pos, type, attrs, marks) {
var node = this.doc.nodeAt(pos);
if (!node) {
throw new RangeError("No node at given position");
}
if (!type) {
type = node.type;
}
var newNode = type.create(attrs, null, marks || node.marks);
if (node.isLeaf) {
return this.replaceWith(pos, pos + node.nodeSize, newNode);
}
if (!type.validContent(node.content, attrs)) {
throw new RangeError("Invalid content for node type " + type.name);
}
return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1, new Slice(Fragment.from(newNode), 0, 0), 1, true));
};
// :: (Node, number, number, ?[?{type: NodeType, attrs: ?Object}]) → bool
// Check whether splitting at the given position is allowed.
function canSplit(doc, pos, depth, typesAfter) {
if (depth === void 0) depth = 1;
var $pos = doc.resolve(pos),
base = $pos.depth - depth;
var innerType = typesAfter && typesAfter[typesAfter.length - 1] || $pos.parent;
if (base < 0 || !$pos.parent.canReplace($pos.index(), $pos.parent.childCount) || !innerType.type.validContent($pos.parent.content.cutByIndex($pos.index(), $pos.parent.childCount), innerType.attrs)) {
return false;
}
for (var d = $pos.depth - 1, i = depth - 2; d > base; d--, i--) {
var node = $pos.node(d),
index$1 = $pos.index(d);
var rest = node.content.cutByIndex(index$1, node.childCount);
var after = typesAfter && typesAfter[i] || node;
if (after != node) {
rest = rest.replaceChild(0, after.type.create(after.attrs));
}
if (!node.canReplace(index$1 + 1, node.childCount) || !after.type.validContent(rest, after.attrs)) {
return false;
}
}
var index = $pos.indexAfter(base);
var 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);
}
exports.canSplit = canSplit;
// :: (number, ?number, ?[?{type: NodeType, attrs: ?Object}]) → this
// Split the node at the given position, and optionally, if `depth` is
// greater than one, any number of nodes above that. By default, the
// parts split off will inherit the node type of the original node.
// This can be changed by passing an array of types and attributes to
// use after the split.
Transform.prototype.split = function (pos, depth, typesAfter) {
if (depth === void 0) depth = 1;
var $pos = this.doc.resolve(pos),
before = Fragment.empty,
after = Fragment.empty;
for (var d = $pos.depth, e = $pos.depth - depth, i = depth - 1; d > e; d--, i--) {
before = Fragment.from($pos.node(d).copy(before));
var typeAfter = typesAfter && typesAfter[i];
after = Fragment.from(typeAfter ? typeAfter.type.create(typeAfter.attrs, after) : $pos.node(d).copy(after));
}
return this.step(new ReplaceStep(pos, pos, new Slice(before.append(after), depth, depth, true)));
};
// :: (Node, number) → bool
// Test whether the blocks before and after a given position can be
// joined.
function canJoin(doc, pos) {
var $pos = doc.resolve(pos),
index = $pos.index();
return joinable($pos.nodeBefore, $pos.nodeAfter) && $pos.parent.canReplace(index, index + 1);
}
exports.canJoin = canJoin;
function joinable(a, b) {
return a && b && !a.isLeaf && a.canAppend(b);
}
// :: (Node, number, ?number) → ?number
// Find an ancestor of the given position that can be joined to the
// block before (or after if `dir` is positive). Returns the joinable
// point, if any.
function joinPoint(doc, pos, dir) {
if (dir === void 0) dir = -1;
var $pos = doc.resolve(pos);
for (var d = $pos.depth;; d--) {
var before = void 0,
after = void 0;
if (d == $pos.depth) {
before = $pos.nodeBefore;
after = $pos.nodeAfter;
} else if (dir > 0) {
before = $pos.node(d + 1);
after = $pos.node(d).maybeChild($pos.index(d) + 1);
} else {
before = $pos.node(d).maybeChild($pos.index(d) - 1);
after = $pos.node(d + 1);
}
if (before && !before.isTextblock && joinable(before, after)) {
return pos;
}
if (d == 0) {
break;
}
pos = dir < 0 ? $pos.before(d) : $pos.after(d);
}
}
exports.joinPoint = joinPoint;
// :: (number, ?number, ?bool) → this
// Join the blocks around the given position. If depth is 2, their
// last and first siblings are also joined, and so on.
Transform.prototype.join = function (pos, depth) {
if (depth === void 0) depth = 1;
var step = new ReplaceStep(pos - depth, pos + depth, Slice.empty, true);
return this.step(step);
};
// :: (Node, number, NodeType, ?Object) → ?number
// Try to find a point where a node of the given type can be inserted
// near `pos`, by searching up the node hierarchy when `pos` itself
// isn't a valid place but is at the start or end of a node. Return
// null if no position was found.
function insertPoint(doc, pos, nodeType, attrs) {
var $pos = doc.resolve(pos);
if ($pos.parent.canReplaceWith($pos.index(), $pos.index(), nodeType, attrs)) {
return pos;
}
if ($pos.parentOffset == 0) {
for (var d = $pos.depth - 1; d >= 0; d--) {
var index = $pos.index(d);
if ($pos.node(d).canReplaceWith(index, index, nodeType, attrs)) {
return $pos.before(d + 1);
}
if (index > 0) {
return null;
}
}
}
if ($pos.parentOffset == $pos.parent.content.size) {
for (var d$1 = $pos.depth - 1; d$1 >= 0; d$1--) {
var index$1 = $pos.indexAfter(d$1);
if ($pos.node(d$1).canReplaceWith(index$1, index$1, nodeType, attrs)) {
return $pos.after(d$1 + 1);
}
if (index$1 < $pos.node(d$1).childCount) {
return null;
}
}
}
}
exports.insertPoint = insertPoint;
},{"./replace_step":24,"./transform":27,"prosemirror-model":7}],27:[function(require,module,exports){
var ref = require("./map");
var Mapping = ref.Mapping;
var TransformError = function (Error) {
function TransformError(message) {
Error.call(this, message);
}
if (Error) TransformError.__proto__ = Error;
TransformError.prototype = Object.create(Error && Error.prototype);
TransformError.prototype.constructor = TransformError;
var prototypeAccessors = { name: {} };
prototypeAccessors.name.get = function () {
return "TransformError";
};
Object.defineProperties(TransformError.prototype, prototypeAccessors);
return TransformError;
}(Error);
exports.TransformError = TransformError;
// ::- Abstraction to build up and track such an array of
// [steps](#transform.Step).
//
// The high-level transforming methods return the `Transform` object
// itself, so that they can be chained.
var Transform = function Transform(doc) {
// :: Node
// The current document (the result of applying the steps in the
// transform).
this.doc = doc;
// :: [Step]
// The steps in this transform.
this.steps = [];
// :: [Node]
// The documents before each of the steps.
this.docs = [];
// :: Mapping
// A mapping with the maps for each of the steps in this transform.
this.mapping = new Mapping();
};
var prototypeAccessors$1 = { before: {}, docChanged: {} };
// :: Node The document at the start of the transformation.
prototypeAccessors$1.before.get = function () {
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.
Transform.prototype.step = function step(object) {
var result = this.maybeStep(object);
if (result.failed) {
throw new TransformError(result.failed);
}
return this;
};
// :: (Step) → StepResult
// Try to apply a step in this transformation, ignoring it if it
// fails. Returns the step result.
Transform.prototype.maybeStep = function maybeStep(step) {
var result = step.apply(this.doc);
if (!result.failed) {
this.addStep(step, result.doc);
}
return result;
};
// :: bool
// True when this transaction changes the document.
prototypeAccessors$1.docChanged.get = function () {
return this.steps.length > 0;
};
Transform.prototype.addStep = function addStep(step, doc) {
this.docs.push(this.doc);
this.steps.push(step);
this.mapping.appendMap(step.getMap());
this.doc = doc;
};
Object.defineProperties(Transform.prototype, prototypeAccessors$1);
exports.Transform = Transform;
},{"./map":20}]},{},[14]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment