| 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 : /home/bitnami/htdocs/app/node_modules/prosemirror-state/src/ |
Upload File : |
import {Node} from "prosemirror-model"
import {Selection} from "./selection"
import {Transaction} from "./transaction"
function bind(f, self) {
return !self || !f ? f : f.bind(self)
}
class FieldDesc {
constructor(name, desc, self) {
this.name = name
this.init = bind(desc.init, self)
this.apply = bind(desc.apply, self)
}
}
const baseFields = [
new FieldDesc("doc", {
init(config) { return config.doc || config.schema.topNodeType.createAndFill() },
apply(tr) { return tr.doc }
}),
new FieldDesc("selection", {
init(config, instance) { return config.selection || Selection.atStart(instance.doc) },
apply(tr) { return tr.selection }
}),
new FieldDesc("storedMarks", {
init(config) { return config.storedMarks || null },
apply(tr, _marks, _old, state) { return state.selection.$cursor ? tr.storedMarks : null }
}),
new FieldDesc("scrollToSelection", {
init() { return 0 },
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.
class Configuration {
constructor(schema, plugins) {
this.schema = schema
this.fields = baseFields.concat()
this.plugins = []
this.pluginsByKey = Object.create(null)
if (plugins) plugins.forEach(plugin => {
if (this.pluginsByKey[plugin.key])
throw new RangeError("Adding different instances of a keyed plugin (" + plugin.key + ")")
this.plugins.push(plugin)
this.pluginsByKey[plugin.key] = plugin
if (plugin.spec.state)
this.fields.push(new FieldDesc(plugin.key, plugin.spec.state, plugin))
})
}
}
// ::- The state of a ProseMirror editor is represented by an object
// of this type. A state is a persistent data structure—it isn't
// updated, but rather a new state value is computed from an old one
// using the [`apply`](#state.EditorState.apply) method.
//
// A state holds a number of built-in fields, and plugins can
// [define](#state.PluginSpec.state) additional fields.
export class EditorState {
constructor(config) {
this.config = config
}
// doc:: Node
// The current document.
// selection:: Selection
// The selection.
// storedMarks:: ?[Mark]
// A set of marks to apply to the next input. Will be null when
// no explicit marks have been set.
// :: Schema
// The schema of the state's document.
get schema() {
return this.config.schema
}
// :: [Plugin]
// The plugins that are active in this state.
get plugins() {
return this.config.plugins
}
// :: (Transaction) → EditorState
// Apply the given transaction to produce a new state.
apply(tr) {
return this.applyTransaction(tr).state
}
// : (Transaction) → bool
filterTransaction(tr, ignore = -1) {
for (let i = 0; i < this.config.plugins.length; i++) if (i != ignore) {
let plugin = this.config.plugins[i]
if (plugin.spec.filterTransaction && !plugin.spec.filterTransaction.call(plugin, tr, this))
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.
applyTransaction(rootTr) {
if (!this.filterTransaction(rootTr)) return {state: this, transactions: []}
let trs = [rootTr], newState = this.applyInner(rootTr), 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 (;;) {
let haveNew = false
for (let i = 0; i < this.config.plugins.length; i++) {
let plugin = this.config.plugins[i]
if (plugin.spec.appendTransaction) {
let n = seen ? seen[i].n : 0, oldState = seen ? seen[i].state : this
let tr = n < trs.length &&
plugin.spec.appendTransaction.call(plugin, n ? trs.slice(n) : trs, oldState, newState)
if (tr && newState.filterTransaction(tr, i)) {
tr.setMeta("appendedTransaction", rootTr)
if (!seen) {
seen = []
for (let j = 0; j < this.config.plugins.length; j++)
seen.push(j < i ? {state: newState, n: trs.length} : {state: this, n: 0})
}
trs.push(tr)
newState = newState.applyInner(tr)
haveNew = true
}
if (seen) seen[i] = {state: newState, n: trs.length}
}
}
if (!haveNew) return {state: newState, transactions: trs}
}
}
// : (Transaction) → EditorState
applyInner(tr) {
if (!tr.before.eq(this.doc)) throw new RangeError("Applying a mismatched transaction")
let newInstance = new EditorState(this.config), fields = this.config.fields
for (let i = 0; i < fields.length; i++) {
let field = fields[i]
newInstance[field.name] = field.apply(tr, this[field.name], this, newInstance)
}
for (let i = 0; i < applyListeners.length; i++) applyListeners[i](this, tr, newInstance)
return newInstance
}
// :: Transaction
// Start a [transaction](#state.Transaction) from this state.
get tr() { return new Transaction(this) }
// :: (Object) → EditorState
// Create a new state.
//
// config::- Configuration options. Must contain `schema` or `doc` (or both).
//
// schema:: ?Schema
// The schema to use (only relevant if no `doc` is specified).
//
// doc:: ?Node
// The starting document.
//
// selection:: ?Selection
// A valid selection in the document.
//
// storedMarks:: ?[Mark]
// The initial set of [stored marks](#state.EditorState.storedMarks).
//
// plugins:: ?[Plugin]
// The plugins that should be active in this state.
static create(config) {
let $config = new Configuration(config.doc ? config.doc.type.schema : config.schema, config.plugins)
let instance = new EditorState($config)
for (let 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
//
// plugins:: [Plugin]
// New set of active plugins.
reconfigure(config) {
let $config = new Configuration(this.schema, config.plugins)
let fields = $config.fields, instance = new EditorState($config)
for (let i = 0; i < fields.length; i++) {
let name = fields[i].name
instance[name] = this.hasOwnProperty(name) ? this[name] : fields[i].init(config, instance)
}
return instance
}
// :: (?union<Object<Plugin>, string, number>) → 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. The argument may also be
// a string or number, in which case it is ignored, to support the
// way `JSON.stringify` calls `toString` methods.
toJSON(pluginFields) {
let result = {doc: this.doc.toJSON(), selection: this.selection.toJSON()}
if (this.storedMarks) result.storedMarks = this.storedMarks.map(m => m.toJSON())
if (pluginFields && typeof pluginFields == 'object') for (let prop in pluginFields) {
if (prop == "doc" || prop == "selection")
throw new RangeError("The JSON fields `doc` and `selection` are reserved")
let plugin = pluginFields[prop], state = plugin.spec.state
if (state && state.toJSON) result[prop] = state.toJSON.call(plugin, this[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.
static fromJSON(config, json, pluginFields) {
if (!json) throw new RangeError("Invalid input for EditorState.fromJSON")
if (!config.schema) throw new RangeError("Required config field 'schema' missing")
let $config = new Configuration(config.schema, config.plugins)
let instance = new EditorState($config)
$config.fields.forEach(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 (field.name == "storedMarks") {
if (json.storedMarks) instance.storedMarks = json.storedMarks.map(config.schema.markFromJSON)
} else {
if (pluginFields) for (let prop in pluginFields) {
let 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.
//
// FIXME this is no longer needed as of prosemirror-view 1.9.0,
// though due to backwards-compat we should probably keep it around
// for a while (if only as a no-op)
static addApplyListener(f) {
applyListeners.push(f)
}
static removeApplyListener(f) {
let found = applyListeners.indexOf(f)
if (found > -1) applyListeners.splice(found, 1)
}
}
const applyListeners = []