Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
import {Transform, Step} from "prosemirror-transform"
import {Mark, MarkType, Node, Slice} from "prosemirror-model"
import {type EditorView} from "prosemirror-view"
import {Selection} from "./selection"
import {Plugin, PluginKey} from "./plugin"
import {EditorState} from "./state"
/// Commands are functions that take a state and a an optional
/// transaction dispatch function and...
///
/// - determine whether they apply to this state
/// - if not, return false
/// - if `dispatch` was passed, perform their effect, possibly by
/// passing a transaction to `dispatch`
/// - return true
///
/// In some cases, the editor view is passed as a third argument.
export type Command = (state: EditorState, dispatch?: (tr: Transaction) => void, view?: EditorView) => boolean
const 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
/// transaction represents, so that they can update their [own
/// state](#state.StateField) accordingly.
///
/// The [editor view](#view.EditorView) uses a few metadata properties:
/// it will attach a property `"pointer"` with the value `true` to
/// selection transactions directly caused by mouse or touch input, and
/// a `"uiEvent"` property of that may be `"paste"`, `"cut"`, or `"drop"`.
export class Transaction extends Transform {
/// The timestamp associated with this transaction, in the same
/// format as `Date.now()`.
time: number
private curSelection: Selection
// The step count for which the current selection is valid.
private curSelectionFor = 0
// Bitfield to track which aspects of the state were updated by
// this transaction.
private updated = 0
// Object used to store metadata properties for the transaction.
private meta: {[name: string]: any} = Object.create(null)
/// The stored marks set by this transaction, if any.
storedMarks: readonly Mark[] | null
/// @internal
constructor(state: EditorState) {
super(state.doc)
this.time = Date.now()
this.curSelection = state.selection
this.storedMarks = state.storedMarks
}
/// The transaction's current selection. This defaults to the editor
/// selection [mapped](#state.Selection.map) through the steps in the
/// transaction, but can be overwritten with
/// [`setSelection`](#state.Transaction.setSelection).
get selection(): Selection {
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
}
/// Update the transaction's current selection. Will determine the
/// selection that the editor gets when the transaction is applied.
setSelection(selection: Selection): this {
if (selection.$from.doc != this.doc)
throw new RangeError("Selection passed to setSelection must point at the current document")
this.curSelection = selection
this.curSelectionFor = this.steps.length
this.updated = (this.updated | UPDATED_SEL) & ~UPDATED_MARKS
this.storedMarks = null
return this
}
/// Whether the selection was explicitly updated by this transaction.
get selectionSet() {
return (this.updated & UPDATED_SEL) > 0
}
/// Set the current stored marks.
setStoredMarks(marks: readonly Mark[] | null): this {
this.storedMarks = marks
this.updated |= UPDATED_MARKS
return this
}
/// 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.
ensureMarks(marks: readonly Mark[]): this {
if (!Mark.sameSet(this.storedMarks || this.selection.$from.marks(), marks))
this.setStoredMarks(marks)
return this
}
/// Add a mark to the set of stored marks.
addStoredMark(mark: Mark): this {
return this.ensureMarks(mark.addToSet(this.storedMarks || this.selection.$head.marks()))
}
/// Remove a mark or mark type from the set of stored marks.
removeStoredMark(mark: Mark | MarkType): this {
return this.ensureMarks(mark.removeFromSet(this.storedMarks || this.selection.$head.marks()))
}
/// Whether the stored marks were explicitly set for this transaction.
get storedMarksSet() {
return (this.updated & UPDATED_MARKS) > 0
}
/// @internal
addStep(step: Step, doc: Node) {
super.addStep(step, doc)
this.updated = this.updated & ~UPDATED_MARKS
this.storedMarks = null
}
/// Update the timestamp for the transaction.
setTime(time: number): this {
this.time = time
return this
}
/// Replace the current selection with the given slice.
replaceSelection(slice: Slice): this {
this.selection.replace(this, slice)
return this
}
/// Replace the selection with the given node. When `inheritMarks` is
/// true and the content is inline, it inherits the marks from the
/// place where it is inserted.
replaceSelectionWith(node: Node, inheritMarks = true): this {
let selection = this.selection
if (inheritMarks)
node = node.mark(this.storedMarks || (selection.empty ? selection.$from.marks() : (selection.$from.marksAcross(selection.$to) || Mark.none)))
selection.replaceWith(this, node)
return this
}
/// Delete the selection.
deleteSelection(): this {
this.selection.replace(this)
return this
}
/// Replace the given range, or the selection if no range is given,
/// with a text node containing the given string.
insertText(text: string, from?: number, to?: number): this {
let schema = this.doc.type.schema
if (from == null) {
if (!text) return this.deleteSelection()
return this.replaceSelectionWith(schema.text(text), true)
} else {
if (to == null) to = from
to = to == null ? from : to
if (!text) return this.deleteRange(from, to)
let marks = this.storedMarks
if (!marks) {
let $from = this.doc.resolve(from)
marks = to == from ? $from.marks() : $from.marksAcross(this.doc.resolve(to))
}
this.replaceRangeWith(from, to, schema.text(text, marks))
if (!this.selection.empty) this.setSelection(Selection.near(this.selection.$to))
return this
}
}
/// Store a metadata property in this transaction, keyed either by
/// name or by plugin.
setMeta(key: string | Plugin | PluginKey, value: any): this {
this.meta[typeof key == "string" ? key : key.key] = value
return this
}
/// Retrieve a metadata property for a given name or plugin.
getMeta(key: string | Plugin | PluginKey) {
return this.meta[typeof key == "string" ? key : key.key]
}
/// Returns true if this transaction doesn't contain any metadata,
/// and can thus safely be extended.
get isGeneric() {
for (let _ in this.meta) return false
return true
}
/// Indicate that the editor should scroll the selection into view
/// when updated to the state produced by this transaction.
scrollIntoView(): this {
this.updated |= UPDATED_SCROLL
return this
}
/// True when this transaction has had `scrollIntoView` called on it.
get scrolledIntoView() {
return (this.updated & UPDATED_SCROLL) > 0
}
}