Created
July 26, 2017 07:28
-
-
Save kurtharriger/8ee9193e8e39f1f607e8cbb13f2c843d to your computer and use it in GitHub Desktop.
prosemirror.state
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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