2022-06-08 20:10:25 +08:00
|
|
|
|
import { MarkType, NodeType, Schema } from 'prosemirror-model'
|
2021-03-30 20:07:18 +08:00
|
|
|
|
import {
|
2021-05-07 17:10:18 +08:00
|
|
|
|
EditorState,
|
|
|
|
|
Plugin,
|
|
|
|
|
PluginKey,
|
|
|
|
|
Transaction,
|
2021-03-30 20:07:18 +08:00
|
|
|
|
} from 'prosemirror-state'
|
2020-09-24 06:29:05 +08:00
|
|
|
|
import { EditorView } from 'prosemirror-view'
|
2022-06-08 20:10:25 +08:00
|
|
|
|
|
|
|
|
|
import { CommandManager } from './CommandManager'
|
|
|
|
|
import { EventEmitter } from './EventEmitter'
|
|
|
|
|
import { ExtensionManager } from './ExtensionManager'
|
|
|
|
|
import * as extensions from './extensions'
|
2021-12-06 19:00:09 +08:00
|
|
|
|
import { createDocument } from './helpers/createDocument'
|
2022-06-08 20:10:25 +08:00
|
|
|
|
import { getAttributes } from './helpers/getAttributes'
|
2021-12-06 19:00:09 +08:00
|
|
|
|
import { getHTMLFromFragment } from './helpers/getHTMLFromFragment'
|
|
|
|
|
import { getText } from './helpers/getText'
|
2022-06-08 20:10:25 +08:00
|
|
|
|
import { getTextSerializersFromSchema } from './helpers/getTextSerializersFromSchema'
|
|
|
|
|
import { isActive } from './helpers/isActive'
|
2021-12-06 19:00:09 +08:00
|
|
|
|
import { isNodeEmpty } from './helpers/isNodeEmpty'
|
|
|
|
|
import { resolveFocusPosition } from './helpers/resolveFocusPosition'
|
2022-06-08 20:10:25 +08:00
|
|
|
|
import { style } from './style'
|
2021-01-28 16:04:55 +08:00
|
|
|
|
import {
|
|
|
|
|
CanCommands,
|
|
|
|
|
ChainedCommands,
|
2022-06-08 20:10:25 +08:00
|
|
|
|
EditorEvents,
|
|
|
|
|
EditorOptions,
|
2021-11-10 07:24:18 +08:00
|
|
|
|
JSONContent,
|
2021-01-28 16:04:55 +08:00
|
|
|
|
SingleCommands,
|
2021-09-30 15:34:45 +08:00
|
|
|
|
TextSerializer,
|
2021-01-28 16:04:55 +08:00
|
|
|
|
} from './types'
|
2022-06-08 20:10:25 +08:00
|
|
|
|
import { createStyleTag } from './utilities/createStyleTag'
|
|
|
|
|
import { isFunction } from './utilities/isFunction'
|
2020-08-21 05:25:55 +08:00
|
|
|
|
|
2020-11-16 23:58:30 +08:00
|
|
|
|
export { extensions }
|
|
|
|
|
|
2020-11-17 04:54:40 +08:00
|
|
|
|
export interface HTMLElement {
|
2020-09-12 00:06:13 +08:00
|
|
|
|
editor?: Editor
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-30 15:25:40 +08:00
|
|
|
|
export class Editor extends EventEmitter<EditorEvents> {
|
2019-12-08 07:16:44 +08:00
|
|
|
|
|
2020-09-23 03:25:32 +08:00
|
|
|
|
private commandManager!: CommandManager
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2021-01-20 03:27:51 +08:00
|
|
|
|
public extensionManager!: ExtensionManager
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2020-08-18 15:36:37 +08:00
|
|
|
|
private css!: HTMLStyleElement
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2020-08-18 15:36:37 +08:00
|
|
|
|
public schema!: Schema
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2020-08-18 15:36:37 +08:00
|
|
|
|
public view!: EditorView
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2020-08-22 04:08:54 +08:00
|
|
|
|
public isFocused = false
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2021-10-22 14:52:54 +08:00
|
|
|
|
public extensionStorage: Record<string, any> = {}
|
|
|
|
|
|
2020-08-18 15:41:31 +08:00
|
|
|
|
public options: EditorOptions = {
|
2020-04-11 20:33:58 +08:00
|
|
|
|
element: document.createElement('div'),
|
2020-03-05 05:40:08 +08:00
|
|
|
|
content: '',
|
|
|
|
|
injectCSS: true,
|
2022-03-07 23:35:06 +08:00
|
|
|
|
injectNonce: undefined,
|
2020-03-06 03:30:58 +08:00
|
|
|
|
extensions: [],
|
2020-11-17 22:47:39 +08:00
|
|
|
|
autofocus: false,
|
2020-08-22 03:53:45 +08:00
|
|
|
|
editable: true,
|
2020-11-18 04:21:19 +08:00
|
|
|
|
editorProps: {},
|
2020-11-18 04:15:10 +08:00
|
|
|
|
parseOptions: {},
|
2020-11-21 04:30:12 +08:00
|
|
|
|
enableInputRules: true,
|
|
|
|
|
enablePasteRules: true,
|
2021-09-22 01:20:38 +08:00
|
|
|
|
enableCoreExtensions: true,
|
2021-04-02 06:07:40 +08:00
|
|
|
|
onBeforeCreate: () => null,
|
2020-11-30 20:56:42 +08:00
|
|
|
|
onCreate: () => null,
|
2020-11-17 22:27:00 +08:00
|
|
|
|
onUpdate: () => null,
|
2021-03-09 16:50:03 +08:00
|
|
|
|
onSelectionUpdate: () => null,
|
2020-11-17 22:27:00 +08:00
|
|
|
|
onTransaction: () => null,
|
|
|
|
|
onFocus: () => null,
|
|
|
|
|
onBlur: () => null,
|
2020-11-30 20:56:42 +08:00
|
|
|
|
onDestroy: () => null,
|
2020-03-05 05:40:08 +08:00
|
|
|
|
}
|
2020-08-11 22:57:11 +08:00
|
|
|
|
|
2020-04-14 16:13:27 +08:00
|
|
|
|
constructor(options: Partial<EditorOptions> = {}) {
|
2020-03-06 06:59:48 +08:00
|
|
|
|
super()
|
2021-03-05 07:02:28 +08:00
|
|
|
|
this.setOptions(options)
|
2020-03-06 04:49:53 +08:00
|
|
|
|
this.createExtensionManager()
|
2021-02-10 21:52:08 +08:00
|
|
|
|
this.createCommandManager()
|
2020-03-06 04:49:53 +08:00
|
|
|
|
this.createSchema()
|
2021-04-28 03:52:22 +08:00
|
|
|
|
this.on('beforeCreate', this.options.onBeforeCreate)
|
2021-04-02 06:07:40 +08:00
|
|
|
|
this.emit('beforeCreate', { editor: this })
|
2020-03-06 04:49:53 +08:00
|
|
|
|
this.createView()
|
2020-09-30 23:12:17 +08:00
|
|
|
|
this.injectCSS()
|
2020-11-30 20:56:42 +08:00
|
|
|
|
this.on('create', this.options.onCreate)
|
2020-11-17 22:27:00 +08:00
|
|
|
|
this.on('update', this.options.onUpdate)
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.on('selectionUpdate', this.options.onSelectionUpdate)
|
2020-11-17 22:27:00 +08:00
|
|
|
|
this.on('transaction', this.options.onTransaction)
|
|
|
|
|
this.on('focus', this.options.onFocus)
|
|
|
|
|
this.on('blur', this.options.onBlur)
|
2020-11-30 20:56:42 +08:00
|
|
|
|
this.on('destroy', this.options.onDestroy)
|
2020-11-17 22:27:00 +08:00
|
|
|
|
|
|
|
|
|
window.setTimeout(() => {
|
2021-05-28 18:14:12 +08:00
|
|
|
|
if (this.isDestroyed) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 22:47:39 +08:00
|
|
|
|
this.commands.focus(this.options.autofocus)
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('create', { editor: this })
|
2020-11-17 22:27:00 +08:00
|
|
|
|
}, 0)
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-22 14:52:54 +08:00
|
|
|
|
/**
|
|
|
|
|
* Returns the editor storage.
|
|
|
|
|
*/
|
|
|
|
|
public get storage(): Record<string, any> {
|
|
|
|
|
return this.extensionStorage
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-13 18:42:04 +08:00
|
|
|
|
/**
|
|
|
|
|
* An object of all registered commands.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public get commands(): SingleCommands {
|
2021-10-14 17:56:40 +08:00
|
|
|
|
return this.commandManager.commands
|
2020-11-13 16:58:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-23 03:25:32 +08:00
|
|
|
|
/**
|
|
|
|
|
* Create a command chain to call multiple commands at once.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public chain(): ChainedCommands {
|
2021-10-14 17:56:40 +08:00
|
|
|
|
return this.commandManager.chain()
|
2020-09-22 00:40:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 00:18:12 +08:00
|
|
|
|
/**
|
|
|
|
|
* Check if a command or a command chain can be executed. Without executing it.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public can(): CanCommands {
|
2021-10-14 17:56:40 +08:00
|
|
|
|
return this.commandManager.can()
|
2020-11-03 00:18:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-30 23:12:17 +08:00
|
|
|
|
/**
|
|
|
|
|
* Inject CSS styles.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private injectCSS(): void {
|
2020-09-30 23:12:17 +08:00
|
|
|
|
if (this.options.injectCSS && document) {
|
2022-03-07 23:35:06 +08:00
|
|
|
|
this.css = createStyleTag(style, this.options.injectNonce)
|
2020-09-30 23:12:17 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Update editor options.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param options A list of options
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public setOptions(options: Partial<EditorOptions> = {}): void {
|
2021-08-09 23:24:18 +08:00
|
|
|
|
this.options = {
|
|
|
|
|
...this.options,
|
|
|
|
|
...options,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.view || !this.state || this.isDestroyed) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-08-09 23:19:50 +08:00
|
|
|
|
|
2021-08-09 23:24:18 +08:00
|
|
|
|
if (this.options.editorProps) {
|
|
|
|
|
this.view.setProps(this.options.editorProps)
|
2021-08-09 23:19:50 +08:00
|
|
|
|
}
|
2021-08-09 23:24:18 +08:00
|
|
|
|
|
|
|
|
|
this.view.updateState(this.state)
|
2021-03-05 07:02:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-05 19:15:50 +08:00
|
|
|
|
/**
|
|
|
|
|
* Update editable state of the editor.
|
|
|
|
|
*/
|
2021-03-05 07:02:28 +08:00
|
|
|
|
public setEditable(editable: boolean): void {
|
|
|
|
|
this.setOptions({ editable })
|
2020-08-22 03:53:45 +08:00
|
|
|
|
}
|
2020-09-03 21:11:55 +08:00
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Returns whether the editor is editable.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public get isEditable(): boolean {
|
2021-08-09 23:40:45 +08:00
|
|
|
|
// since plugins are applied after creating the view
|
|
|
|
|
// `editable` is always `true` for one tick.
|
|
|
|
|
// that’s why we also have to check for `options.editable`
|
|
|
|
|
return this.options.editable
|
|
|
|
|
&& this.view
|
|
|
|
|
&& this.view.editable
|
2020-08-22 04:08:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Returns the editor state.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public get state(): EditorState {
|
2020-03-29 07:21:28 +08:00
|
|
|
|
return this.view.state
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Register a ProseMirror plugin.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param plugin A ProseMirror plugin
|
|
|
|
|
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public registerPlugin(plugin: Plugin, handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[]): void {
|
2021-10-08 21:02:09 +08:00
|
|
|
|
const plugins = isFunction(handlePlugins)
|
2020-04-13 02:17:56 +08:00
|
|
|
|
? handlePlugins(plugin, this.state.plugins)
|
2021-01-29 00:39:57 +08:00
|
|
|
|
: [...this.state.plugins, plugin]
|
2020-04-11 03:43:23 +08:00
|
|
|
|
|
2020-04-13 02:17:56 +08:00
|
|
|
|
const state = this.state.reconfigure({ plugins })
|
2020-04-11 03:43:23 +08:00
|
|
|
|
|
|
|
|
|
this.view.updateState(state)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Unregister a ProseMirror plugin.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2021-09-30 15:25:40 +08:00
|
|
|
|
* @param nameOrPluginKey The plugins name
|
2020-08-22 05:35:15 +08:00
|
|
|
|
*/
|
2021-03-30 20:07:18 +08:00
|
|
|
|
public unregisterPlugin(nameOrPluginKey: string | PluginKey): void {
|
|
|
|
|
if (this.isDestroyed) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const name = typeof nameOrPluginKey === 'string'
|
|
|
|
|
? `${nameOrPluginKey}$`
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
: nameOrPluginKey.key
|
|
|
|
|
|
2020-04-11 04:55:14 +08:00
|
|
|
|
const state = this.state.reconfigure({
|
|
|
|
|
// @ts-ignore
|
2021-03-30 20:07:18 +08:00
|
|
|
|
plugins: this.state.plugins.filter(plugin => !plugin.key.startsWith(name)),
|
2020-04-11 04:55:14 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.view.updateState(state)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Creates an extension manager.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private createExtensionManager(): void {
|
2021-09-22 01:20:38 +08:00
|
|
|
|
const coreExtensions = this.options.enableCoreExtensions
|
2021-09-22 02:21:12 +08:00
|
|
|
|
? Object.values(extensions)
|
2021-09-22 01:20:38 +08:00
|
|
|
|
: []
|
2021-01-29 00:39:57 +08:00
|
|
|
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
2020-12-22 00:43:29 +08:00
|
|
|
|
return ['extension', 'node', 'mark'].includes(extension?.type)
|
|
|
|
|
})
|
2020-10-23 16:44:30 +08:00
|
|
|
|
|
2021-02-17 01:54:44 +08:00
|
|
|
|
this.extensionManager = new ExtensionManager(allExtensions, this)
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-23 03:25:32 +08:00
|
|
|
|
/**
|
|
|
|
|
* Creates an command manager.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private createCommandManager(): void {
|
2021-10-14 17:56:40 +08:00
|
|
|
|
this.commandManager = new CommandManager({
|
|
|
|
|
editor: this,
|
|
|
|
|
})
|
2020-09-23 03:25:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Creates a ProseMirror schema.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private createSchema(): void {
|
2020-09-10 06:09:05 +08:00
|
|
|
|
this.schema = this.extensionManager.schema
|
2020-03-06 04:05:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Creates a ProseMirror view.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private createView(): void {
|
2021-12-03 17:35:17 +08:00
|
|
|
|
const doc = createDocument(this.options.content, this.schema, this.options.parseOptions)
|
|
|
|
|
const selection = resolveFocusPosition(doc, this.options.autofocus)
|
2021-12-03 17:36:51 +08:00
|
|
|
|
|
2020-04-11 20:33:58 +08:00
|
|
|
|
this.view = new EditorView(this.options.element, {
|
2020-11-18 04:21:19 +08:00
|
|
|
|
...this.options.editorProps,
|
2020-11-04 22:31:42 +08:00
|
|
|
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
2020-03-06 04:49:53 +08:00
|
|
|
|
state: EditorState.create({
|
2021-12-03 17:35:17 +08:00
|
|
|
|
doc,
|
|
|
|
|
selection,
|
2020-03-06 04:49:53 +08:00
|
|
|
|
}),
|
2020-10-30 18:08:23 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// `editor.view` is not yet available at this time.
|
2020-11-04 22:31:42 +08:00
|
|
|
|
// Therefore we will add all plugins and node views directly afterwards.
|
|
|
|
|
const newState = this.state.reconfigure({
|
2021-04-08 04:07:36 +08:00
|
|
|
|
plugins: this.extensionManager.plugins,
|
2020-11-04 22:31:42 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
this.view.updateState(newState)
|
|
|
|
|
|
2020-12-14 19:05:46 +08:00
|
|
|
|
this.createNodeViews()
|
2020-09-12 00:06:13 +08:00
|
|
|
|
|
2020-10-30 18:08:23 +08:00
|
|
|
|
// Let’s store the editor instance in the DOM element.
|
|
|
|
|
// So we’ll have access to it for tests.
|
2020-09-12 00:06:13 +08:00
|
|
|
|
const dom = this.view.dom as HTMLElement
|
2021-12-03 07:03:39 +08:00
|
|
|
|
|
2021-02-17 01:54:44 +08:00
|
|
|
|
dom.editor = this
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-14 19:05:46 +08:00
|
|
|
|
/**
|
|
|
|
|
* Creates all node views.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public createNodeViews(): void {
|
2020-12-14 19:05:46 +08:00
|
|
|
|
this.view.setProps({
|
|
|
|
|
nodeViews: this.extensionManager.nodeViews,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-09 17:06:13 +08:00
|
|
|
|
public isCapturingTransaction = false
|
|
|
|
|
|
|
|
|
|
private capturedTransaction: Transaction | null = null
|
|
|
|
|
|
|
|
|
|
public captureTransaction(fn: Function) {
|
|
|
|
|
this.isCapturingTransaction = true
|
|
|
|
|
fn()
|
|
|
|
|
this.isCapturingTransaction = false
|
|
|
|
|
|
|
|
|
|
const tr = this.capturedTransaction
|
|
|
|
|
|
|
|
|
|
this.capturedTransaction = null
|
|
|
|
|
|
|
|
|
|
return tr
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* The callback over which to send transactions (state updates) produced by the view.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param transaction An editor state transaction
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
private dispatchTransaction(transaction: Transaction): void {
|
2021-02-09 17:06:13 +08:00
|
|
|
|
if (this.isCapturingTransaction) {
|
|
|
|
|
if (!this.capturedTransaction) {
|
|
|
|
|
this.capturedTransaction = transaction
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
transaction.steps.forEach(step => this.capturedTransaction?.step(step))
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-05 05:40:08 +08:00
|
|
|
|
const state = this.state.apply(transaction)
|
2020-11-27 21:52:19 +08:00
|
|
|
|
const selectionHasChanged = !this.state.selection.eq(state.selection)
|
2020-11-17 22:27:00 +08:00
|
|
|
|
|
2020-03-05 05:40:08 +08:00
|
|
|
|
this.view.updateState(state)
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('transaction', {
|
|
|
|
|
editor: this,
|
|
|
|
|
transaction,
|
|
|
|
|
})
|
2020-08-11 22:57:11 +08:00
|
|
|
|
|
2020-11-27 21:52:19 +08:00
|
|
|
|
if (selectionHasChanged) {
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('selectionUpdate', {
|
|
|
|
|
editor: this,
|
2021-08-13 20:00:50 +08:00
|
|
|
|
transaction,
|
2021-03-09 16:50:03 +08:00
|
|
|
|
})
|
2020-11-27 21:52:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 22:27:00 +08:00
|
|
|
|
const focus = transaction.getMeta('focus')
|
|
|
|
|
const blur = transaction.getMeta('blur')
|
|
|
|
|
|
|
|
|
|
if (focus) {
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('focus', {
|
|
|
|
|
editor: this,
|
|
|
|
|
event: focus.event,
|
2021-08-13 20:00:50 +08:00
|
|
|
|
transaction,
|
2021-03-09 16:50:03 +08:00
|
|
|
|
})
|
2020-11-17 22:27:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (blur) {
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('blur', {
|
|
|
|
|
editor: this,
|
|
|
|
|
event: blur.event,
|
2021-08-13 20:00:50 +08:00
|
|
|
|
transaction,
|
2021-03-09 16:50:03 +08:00
|
|
|
|
})
|
2020-11-17 22:27:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-08 07:16:44 +08:00
|
|
|
|
if (!transaction.docChanged || transaction.getMeta('preventUpdate')) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-09 16:50:03 +08:00
|
|
|
|
this.emit('update', {
|
|
|
|
|
editor: this,
|
|
|
|
|
transaction,
|
|
|
|
|
})
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|
2019-12-17 06:51:18 +08:00
|
|
|
|
|
2021-05-07 17:10:18 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get attributes of the currently selected node or mark.
|
|
|
|
|
*/
|
|
|
|
|
public getAttributes(nameOrType: string | NodeType | MarkType): Record<string, any> {
|
|
|
|
|
return getAttributes(this.state, nameOrType)
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Returns if the currently selected node or mark is active.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param name Name of the node or mark
|
2020-11-30 07:04:30 +08:00
|
|
|
|
* @param attributes Attributes of the node or mark
|
2020-08-22 05:35:15 +08:00
|
|
|
|
*/
|
2020-11-30 07:04:30 +08:00
|
|
|
|
public isActive(name: string, attributes?: {}): boolean;
|
|
|
|
|
public isActive(attributes: {}): boolean;
|
|
|
|
|
public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {
|
|
|
|
|
const name = typeof nameOrAttributes === 'string'
|
|
|
|
|
? nameOrAttributes
|
|
|
|
|
: null
|
|
|
|
|
|
|
|
|
|
const attributes = typeof nameOrAttributes === 'string'
|
|
|
|
|
? attributesOrUndefined
|
|
|
|
|
: nameOrAttributes
|
|
|
|
|
|
|
|
|
|
return isActive(this.state, name, attributes)
|
2020-03-06 04:49:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get the document as JSON.
|
|
|
|
|
*/
|
2021-11-10 07:24:18 +08:00
|
|
|
|
public getJSON(): JSONContent {
|
2020-03-04 17:21:48 +08:00
|
|
|
|
return this.state.doc.toJSON()
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get the document as HTML.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public getHTML(): string {
|
2021-09-29 03:34:57 +08:00
|
|
|
|
return getHTMLFromFragment(this.state.doc.content, this.schema)
|
2020-03-04 17:21:48 +08:00
|
|
|
|
}
|
2020-03-05 04:45:49 +08:00
|
|
|
|
|
2021-09-10 05:51:05 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get the document as text.
|
|
|
|
|
*/
|
|
|
|
|
public getText(options?: {
|
|
|
|
|
blockSeparator?: string,
|
|
|
|
|
textSerializers?: Record<string, TextSerializer>,
|
|
|
|
|
}): string {
|
|
|
|
|
const {
|
|
|
|
|
blockSeparator = '\n\n',
|
|
|
|
|
textSerializers = {},
|
|
|
|
|
} = options || {}
|
|
|
|
|
|
|
|
|
|
return getText(this.state.doc, {
|
|
|
|
|
blockSeparator,
|
|
|
|
|
textSerializers: {
|
|
|
|
|
...textSerializers,
|
2022-05-03 04:01:07 +08:00
|
|
|
|
...getTextSerializersFromSchema(this.schema),
|
2021-09-10 05:51:05 +08:00
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-23 20:28:25 +08:00
|
|
|
|
/**
|
|
|
|
|
* Check if there is no content.
|
|
|
|
|
*/
|
2021-03-25 05:23:08 +08:00
|
|
|
|
public get isEmpty(): boolean {
|
|
|
|
|
return isNodeEmpty(this.state.doc)
|
2020-10-23 20:28:25 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-27 18:40:49 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get the number of characters for the current document.
|
2021-12-09 04:26:30 +08:00
|
|
|
|
*
|
|
|
|
|
* @deprecated
|
2021-01-27 18:40:49 +08:00
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public getCharacterCount(): number {
|
2021-12-09 04:26:30 +08:00
|
|
|
|
console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.')
|
|
|
|
|
|
2021-01-27 18:40:49 +08:00
|
|
|
|
return this.state.doc.content.size - 2
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Destroy the editor.
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public destroy(): void {
|
2020-11-30 20:50:06 +08:00
|
|
|
|
this.emit('destroy')
|
|
|
|
|
|
2020-03-31 19:07:57 +08:00
|
|
|
|
if (this.view) {
|
|
|
|
|
this.view.destroy()
|
2020-03-05 04:45:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 06:59:48 +08:00
|
|
|
|
this.removeAllListeners()
|
2020-03-05 04:45:49 +08:00
|
|
|
|
}
|
2020-09-24 06:29:05 +08:00
|
|
|
|
|
2020-10-01 04:43:58 +08:00
|
|
|
|
/**
|
|
|
|
|
* Check if the editor is already destroyed.
|
|
|
|
|
*/
|
2021-03-05 07:02:28 +08:00
|
|
|
|
public get isDestroyed(): boolean {
|
2020-10-01 04:43:58 +08:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
return !this.view?.docView
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|