2021-03-30 20:07:18 +08:00
|
|
|
|
import {
|
|
|
|
|
EditorState, Plugin, PluginKey, Transaction,
|
|
|
|
|
} from 'prosemirror-state'
|
2020-09-24 06:29:05 +08:00
|
|
|
|
import { EditorView } from 'prosemirror-view'
|
2021-04-07 17:53:37 +08:00
|
|
|
|
import { Schema } from 'prosemirror-model'
|
2020-11-30 16:42:53 +08:00
|
|
|
|
import getNodeAttributes from './helpers/getNodeAttributes'
|
|
|
|
|
import getMarkAttributes from './helpers/getMarkAttributes'
|
|
|
|
|
import isActive from './helpers/isActive'
|
|
|
|
|
import removeElement from './utilities/removeElement'
|
2021-04-07 17:53:37 +08:00
|
|
|
|
import createDocument from './helpers/createDocument'
|
2020-11-30 16:42:53 +08:00
|
|
|
|
import getHTMLFromFragment from './helpers/getHTMLFromFragment'
|
2021-03-25 05:23:08 +08:00
|
|
|
|
import isNodeEmpty from './helpers/isNodeEmpty'
|
2020-11-30 16:42:53 +08:00
|
|
|
|
import createStyleTag from './utilities/createStyleTag'
|
2020-09-23 03:25:32 +08:00
|
|
|
|
import CommandManager from './CommandManager'
|
2020-03-06 04:05:01 +08:00
|
|
|
|
import ExtensionManager from './ExtensionManager'
|
2020-04-02 03:15:23 +08:00
|
|
|
|
import EventEmitter from './EventEmitter'
|
2021-01-28 16:04:55 +08:00
|
|
|
|
import {
|
|
|
|
|
EditorOptions,
|
|
|
|
|
CanCommands,
|
|
|
|
|
ChainedCommands,
|
|
|
|
|
SingleCommands,
|
|
|
|
|
AnyObject,
|
|
|
|
|
} from './types'
|
2020-10-23 16:44:30 +08:00
|
|
|
|
import * as extensions from './extensions'
|
2020-09-30 23:12:17 +08:00
|
|
|
|
import style from './style'
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 06:59:48 +08:00
|
|
|
|
export class Editor extends EventEmitter {
|
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-04-01 22:21:47 +08:00
|
|
|
|
private resizeObserver!: ResizeObserver
|
|
|
|
|
|
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,
|
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,
|
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,
|
|
|
|
|
onViewUpdate: () => null,
|
2020-11-17 22:27:00 +08:00
|
|
|
|
onTransaction: () => null,
|
|
|
|
|
onFocus: () => null,
|
|
|
|
|
onBlur: () => null,
|
2021-04-01 22:21:47 +08:00
|
|
|
|
onResize: () => 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()
|
|
|
|
|
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)
|
|
|
|
|
this.on('viewUpdate', this.options.onViewUpdate)
|
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(() => {
|
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 })
|
2021-04-01 22:21:47 +08:00
|
|
|
|
|
|
|
|
|
if (window.ResizeObserver) {
|
|
|
|
|
this.resizeObserver = new ResizeObserver(() => {
|
|
|
|
|
this.emit('resize', { editor: this })
|
|
|
|
|
})
|
|
|
|
|
this.resizeObserver.observe(this.view.dom)
|
|
|
|
|
}
|
2020-11-17 22:27:00 +08:00
|
|
|
|
}, 0)
|
2021-04-01 22:21:47 +08:00
|
|
|
|
|
2019-12-08 07:16:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
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 {
|
2020-11-13 18:42:04 +08:00
|
|
|
|
return this.commandManager.createCommands()
|
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 {
|
2020-09-23 03:25:32 +08:00
|
|
|
|
return this.commandManager.createChain()
|
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 {
|
2020-11-03 00:18:12 +08:00
|
|
|
|
return this.commandManager.createCan()
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
this.css = createStyleTag(style)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 {
|
2020-08-22 03:53:45 +08:00
|
|
|
|
this.options = { ...this.options, ...options }
|
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-10-01 04:43:58 +08:00
|
|
|
|
if (this.view && this.state && !this.isDestroyed) {
|
2020-08-22 03:53:45 +08:00
|
|
|
|
this.view.updateState(this.state)
|
|
|
|
|
}
|
|
|
|
|
}
|
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 {
|
2020-08-22 04:08:54 +08:00
|
|
|
|
return this.view && this.view.editable
|
|
|
|
|
}
|
|
|
|
|
|
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 {
|
2020-04-13 02:17:56 +08:00
|
|
|
|
const plugins = typeof handlePlugins === 'function'
|
|
|
|
|
? 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
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param name The plugins name
|
|
|
|
|
*/
|
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 {
|
2020-11-16 16:43:17 +08:00
|
|
|
|
const coreExtensions = Object.entries(extensions).map(([, extension]) => extension)
|
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-02-17 01:54:44 +08:00
|
|
|
|
this.commandManager = new CommandManager(this, this.extensionManager.commands)
|
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 {
|
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-04-07 17:53:37 +08:00
|
|
|
|
doc: createDocument(this.options.content, this.schema, this.options.parseOptions),
|
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-03-05 07:02:28 +08:00
|
|
|
|
plugins: [
|
|
|
|
|
new Plugin({
|
|
|
|
|
view: () => ({
|
2021-03-09 16:50:03 +08:00
|
|
|
|
update: () => this.emit('viewUpdate', {
|
|
|
|
|
editor: this,
|
|
|
|
|
}),
|
2021-03-05 07:02:28 +08:00
|
|
|
|
}),
|
|
|
|
|
}),
|
|
|
|
|
...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-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-03-15 20:27:52 +08:00
|
|
|
|
if (transaction.docChanged && !this.isEditable) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
})
|
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,
|
|
|
|
|
})
|
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,
|
|
|
|
|
})
|
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
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get attributes of the currently selected node.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param name Name of the node
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public getNodeAttributes(name: string): AnyObject {
|
2020-11-30 16:21:31 +08:00
|
|
|
|
return getNodeAttributes(this.state, name)
|
2020-04-11 17:45:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-22 05:35:15 +08:00
|
|
|
|
/**
|
|
|
|
|
* Get attributes of the currently selected mark.
|
2020-09-03 21:11:55 +08:00
|
|
|
|
*
|
2020-08-22 05:35:15 +08:00
|
|
|
|
* @param name Name of the mark
|
|
|
|
|
*/
|
2021-01-28 16:04:55 +08:00
|
|
|
|
public getMarkAttributes(name: string): AnyObject {
|
2020-11-30 16:21:31 +08:00
|
|
|
|
return getMarkAttributes(this.state, name)
|
2020-03-31 18:53:52 +08:00
|
|
|
|
}
|
2020-03-30 20:49:48 +08:00
|
|
|
|
|
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-01-28 16:04:55 +08:00
|
|
|
|
public getJSON(): AnyObject {
|
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 {
|
2020-10-29 00:20:38 +08:00
|
|
|
|
return getHTMLFromFragment(this.state.doc, this.schema)
|
2020-03-04 17:21:48 +08:00
|
|
|
|
}
|
2020-03-05 04:45:49 +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-01-28 16:04:55 +08:00
|
|
|
|
public getCharacterCount(): number {
|
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 {
|
2021-04-01 22:21:47 +08:00
|
|
|
|
this.resizeObserver?.unobserve(this.view.dom)
|
|
|
|
|
|
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-31 19:06:34 +08:00
|
|
|
|
removeElement(this.css)
|
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
|
|
|
|
}
|