| Server IP : 34.87.99.72 / Your IP : 216.73.217.31 Web Server : Apache/2.4.46 (Unix) OpenSSL/1.1.1n System : Linux zlock-bitnami-lamp-prod-v2-vm 4.19.0-16-cloud-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 User : bitnami ( 1000) PHP Version : 7.4.18 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /opt/bitnami/apache/htdocs/app/node_modules/prosemirror-model/src/ |
Upload File : |
import OrderedMap from "orderedmap"
import {Node, TextNode} from "./node"
import {Fragment} from "./fragment"
import {Mark} from "./mark"
import {ContentMatch} from "./content"
// 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) {
let defaults = Object.create(null)
for (let attrName in attrs) {
let attr = attrs[attrName]
if (!attr.hasDefault) return null
defaults[attrName] = attr.default
}
return defaults
}
function computeAttrs(attrs, value) {
let built = Object.create(null)
for (let name in attrs) {
let given = value && value[name]
if (given === undefined) {
let attr = attrs[name]
if (attr.hasDefault) given = attr.default
else throw new RangeError("No value supplied for attribute " + name)
}
built[name] = given
}
return built
}
function initAttrs(attrs) {
let result = Object.create(null)
if (attrs) for (let name in attrs) result[name] = new Attribute(attrs[name])
return result
}
// ::- Node types are objects allocated once per `Schema` and used to
// [tag](#model.Node.type) `Node` instances. They contain information
// about the node type, such as its name and what kind of node it
// represents.
export class NodeType {
constructor(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)
// :: ContentMatch
// The starting match of the node type's content expression.
this.contentMatch = null
// : ?[MarkType]
// The set of marks allowed in this node. `null` means all marks
// are allowed.
this.markSet = null
// :: bool
// True if this node type has inline content.
this.inlineContent = 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"
}
// :: bool
// True if this is an inline type.
get isInline() { return !this.isBlock }
// :: bool
// True if this is a textblock type, a block that contains inline
// content.
get isTextblock() { return this.isBlock && this.inlineContent }
// :: bool
// True for node types that allow no content.
get isLeaf() { return this.contentMatch == ContentMatch.empty }
// :: bool
// True when this node is an atom, i.e. when it does not have
// directly editable content.
get isAtom() { return this.isLeaf || this.spec.atom }
// :: () → bool
// Tells you whether this node type has any required attributes.
hasRequiredAttrs() {
for (let n in this.attrs) if (this.attrs[n].isRequired) return true
return false
}
compatibleContent(other) {
return this == other || this.contentMatch.compatible(other.contentMatch)
}
computeAttrs(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.
create(attrs, content, marks) {
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.
createChecked(attrs, content, marks) {
content = Fragment.from(content)
if (!this.validContent(content))
throw new RangeError("Invalid content for node " + this.name)
return new Node(this, this.computeAttrs(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.
createAndFill(attrs, content, marks) {
attrs = this.computeAttrs(attrs)
content = Fragment.from(content)
if (content.size) {
let before = this.contentMatch.fillBefore(content)
if (!before) return null
content = before.append(content)
}
let after = this.contentMatch.matchFragment(content).fillBefore(Fragment.empty, true)
if (!after) return null
return new Node(this, attrs, content.append(after), Mark.setFrom(marks))
}
// :: (Fragment) → bool
// Returns true if the given fragment is valid content for this node
// type with the given attributes.
validContent(content) {
let result = this.contentMatch.matchFragment(content)
if (!result || !result.validEnd) return false
for (let i = 0; i < content.childCount; i++)
if (!this.allowsMarks(content.child(i).marks)) return false
return true
}
// :: (MarkType) → bool
// Check whether the given mark type is allowed in this node.
allowsMarkType(markType) {
return this.markSet == null || this.markSet.indexOf(markType) > -1
}
// :: ([Mark]) → bool
// Test whether the given set of marks are allowed in this node.
allowsMarks(marks) {
if (this.markSet == null) return true
for (let i = 0; i < marks.length; i++) if (!this.allowsMarkType(marks[i].type)) return false
return true
}
// :: ([Mark]) → [Mark]
// Removes the marks that are not allowed in this node from the given set.
allowedMarks(marks) {
if (this.markSet == null) return marks
let copy
for (let i = 0; i < marks.length; i++) {
if (!this.allowsMarkType(marks[i].type)) {
if (!copy) copy = marks.slice(0, i)
} else if (copy) {
copy.push(marks[i])
}
}
return !copy ? marks : copy.length ? copy : Mark.empty
}
static compile(nodes, schema) {
let result = Object.create(null)
nodes.forEach((name, spec) => result[name] = new NodeType(name, schema, spec))
let 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 (let _ in result.text.attrs) throw new RangeError("The text node type should not have attributes")
return result
}
}
// Attribute descriptors
class Attribute {
constructor(options) {
this.hasDefault = Object.prototype.hasOwnProperty.call(options, "default")
this.default = options.default
}
get isRequired() {
return !this.hasDefault
}
}
// Marks
// ::- Like nodes, marks (which are associated with nodes to signify
// things like emphasis or being part of a link) are
// [tagged](#model.Mark.type) with type objects, which are
// instantiated once per `Schema`.
export class MarkType {
constructor(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
let 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.
create(attrs) {
if (!attrs && this.instance) return this.instance
return new Mark(this, computeAttrs(this.attrs, attrs))
}
static compile(marks, schema) {
let result = Object.create(null), rank = 0
marks.forEach((name, spec) => 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.
removeFromSet(set) {
for (var i = 0; i < set.length; i++) if (set[i].type == this) {
set = set.slice(0, i).concat(set.slice(i + 1))
i--
}
return set
}
// :: ([Mark]) → ?Mark
// Tests whether there is a mark of this type in the given set.
isInSet(set) {
for (let i = 0; i < set.length; i++)
if (set[i].type == this) return set[i]
}
// :: (MarkType) → bool
// Queries whether a given mark type is
// [excluded](#model.MarkSpec.excludes) by this one.
excludes(other) {
return this.excluded.indexOf(other) > -1
}
}
// SchemaSpec:: interface
// An object describing a schema, as passed to the [`Schema`](#model.Schema)
// constructor.
//
// nodes:: union<Object<NodeSpec>, OrderedMap<NodeSpec>>
// The node types in this schema. Maps names to
// [`NodeSpec`](#model.NodeSpec) objects that describe the node type
// associated with that name. Their order is significant—it
// determines which [parse rules](#model.NodeSpec.parseDOM) take
// precedence by default, and which nodes come first in a given
// [group](#model.NodeSpec.group).
//
// marks:: ?union<Object<MarkSpec>, OrderedMap<MarkSpec>>
// The mark types that exist in this schema. The order in which they
// are provided determines the order in which [mark
// sets](#model.Mark.addToSet) are sorted and in which [parse
// rules](#model.MarkSpec.parseDOM) are tried.
//
// 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/guide/#schema.content_expressions). When not given,
// the node does not allow any content.
//
// marks:: ?string
// The marks that are allowed inside of this node. May be a
// space-separated string referring to mark names or groups, `"_"`
// to explicitly allow all marks, or `""` to disallow marks. When
// not given, nodes with inline content default to allowing all
// marks, other nodes default to not allowing marks.
//
// group:: ?string
// The group or space-separated groups to which this node belongs,
// which can be referred to in the content expressions for the
// schema.
//
// inline:: ?bool
// Should be set to true 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, in _inserted_ content the defining parents of the
// content are preserved when possible. Typically,
// non-default-paragraph textblock types, and possibly 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 enabled 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 a DOM node or an [array
// structure](#model.DOMOutputSpec) that describes one, 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.
//
// toDebugString:: ?(node: Node) -> string
// Defines the default way a node of this type should be serialized
// to a string representation for debugging (e.g. in error messages).
// 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 its end (or at its start when that is also the start of the
// parent node). 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 mark belongs.
//
// spanning:: ?bool
// Determines whether marks of this type can span multiple adjacent
// nodes when serialized to DOM/HTML. Defaults to true.
//
// toDOM:: ?(mark: Mark, inline: bool) → DOMOutputSpec
// Defines the default way marks of this type should be serialized
// to DOM/HTML. When the resulting spec contains a hole, that is
// where the marked content is placed. Otherwise, it is appended to
// the top node.
//
// 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](#model.NodeSpec.attrs) attributes on nodes or
// marks.
//
// default:: ?any
// The default value for this attribute, to use when no explicit
// value is provided. Attributes that have no default must be
// provided whenever a node or mark of a type that has them is
// created.
// ::- A document schema. Holds [node](#model.NodeType) and [mark
// type](#model.MarkType) objects for the nodes and marks that may
// occur in conforming documents, and provides functionality for
// creating and deserializing such documents.
export class Schema {
// :: (SchemaSpec)
// Construct a schema from a schema [specification](#model.SchemaSpec).
constructor(spec) {
// :: 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).
this.spec = {}
for (let prop in spec) this.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)
let contentExprCache = Object.create(null)
for (let prop in this.nodes) {
if (prop in this.marks)
throw new RangeError(prop + " can not be both a node and a mark")
let type = this.nodes[prop], contentExpr = type.spec.content || "", markExpr = type.spec.marks
type.contentMatch = contentExprCache[contentExpr] ||
(contentExprCache[contentExpr] = ContentMatch.parse(contentExpr, this.nodes))
type.inlineContent = type.contentMatch.inlineContent
type.markSet = markExpr == "_" ? null :
markExpr ? gatherMarks(this, markExpr.split(" ")) :
markExpr == "" || !type.inlineContent ? [] : null
}
for (let prop in this.marks) {
let type = this.marks[prop], excl = type.spec.excludes
type.excluded = excl == null ? [type] : excl == "" ? [] : gatherMarks(this, excl.split(" "))
}
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"]
// :: 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)
}
// :: (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.
node(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.
text(text, marks) {
let type = this.nodes.text
return new TextNode(type, type.defaultAttrs, text, Mark.setFrom(marks))
}
// :: (union<string, MarkType>, ?Object) → Mark
// Create a mark with the given type and attributes.
mark(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.
nodeFromJSON(json) {
return Node.fromJSON(this, json)
}
// :: (Object) → Mark
// Deserialize a mark from its JSON representation. This method is
// bound.
markFromJSON(json) {
return Mark.fromJSON(this, json)
}
nodeType(name) {
let found = this.nodes[name]
if (!found) throw new RangeError("Unknown node type: " + name)
return found
}
}
function gatherMarks(schema, marks) {
let found = []
for (let i = 0; i < marks.length; i++) {
let name = marks[i], mark = schema.marks[name], ok = mark
if (mark) {
found.push(mark)
} else {
for (let prop in schema.marks) {
let mark = schema.marks[prop]
if (name == "_" || (mark.spec.group && mark.spec.group.split(" ").indexOf(name) > -1))
found.push(ok = mark)
}
}
if (!ok) throw new SyntaxError("Unknown mark type: '" + marks[i] + "'")
}
return found
}