mirror of
https://github.com/codex-team/editor.js
synced 2024-05-06 08:33:20 +02:00
initial
This commit is contained in:
parent
b89f756a03
commit
6047e83272
25372
dist/codex-editor.js
vendored
25372
dist/codex-editor.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
|||
import {EditorModules} from '../types-internal/editor-modules';
|
||||
import {EditorConfig} from '../../types';
|
||||
import {ModuleConfig} from '../types-internal/module-config';
|
||||
import {EventEmitter} from './eventEmitter';
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
|
@ -11,7 +12,7 @@ import {ModuleConfig} from '../types-internal/module-config';
|
|||
* @property {Object} config - Editor user settings
|
||||
* @property {EditorModules} Editor - List of Editor modules
|
||||
*/
|
||||
export default class Module {
|
||||
export default class Module extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Editor modules list
|
||||
|
@ -30,6 +31,8 @@ export default class Module {
|
|||
* @param {EditorConfig}
|
||||
*/
|
||||
constructor({config}: ModuleConfig) {
|
||||
super();
|
||||
|
||||
if (new.target === Module) {
|
||||
throw new TypeError('Constructors for abstract class Module are not allowed.');
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ export default class DeleteTune implements BlockTune {
|
|||
public render() {
|
||||
this.nodes.button = $.make('div', [this.CSS.button, this.CSS.buttonDelete], {});
|
||||
this.nodes.button.appendChild($.svg('cross', 12, 12));
|
||||
this.api.listeners.on(this.nodes.button, 'click', (event: MouseEvent) => this.handleClick(event), false);
|
||||
this.api.listeners.add(this.nodes.button, 'click', (event: MouseEvent) => this.handleClick(event), false);
|
||||
return this.nodes.button;
|
||||
}
|
||||
|
||||
|
@ -85,14 +85,14 @@ export default class DeleteTune implements BlockTune {
|
|||
* When toolbar block settings is closed but block deletion is not confirmed,
|
||||
* then reset confirmation state
|
||||
*/
|
||||
this.api.events.on('block-settings-closed', this.resetConfirmation);
|
||||
this.api.toolbar.on('block-settings/closed', this.resetConfirmation);
|
||||
|
||||
} else {
|
||||
|
||||
/**
|
||||
* Unsubscribe from block-settings closing event
|
||||
*/
|
||||
this.api.events.off('block-settings-closed', this.resetConfirmation);
|
||||
this.api.toolbar.off('block-settings/closed', this.resetConfirmation);
|
||||
|
||||
this.api.blocks.delete();
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ export default class MoveDownTune implements BlockTune {
|
|||
public render() {
|
||||
const moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
|
||||
moveDownButton.appendChild($.svg('arrow-down', 14, 14));
|
||||
this.api.listeners.on(
|
||||
this.api.listeners.add(
|
||||
moveDownButton,
|
||||
'click',
|
||||
(event) => this.handleClick(event as MouseEvent, moveDownButton),
|
||||
|
|
|
@ -41,7 +41,7 @@ export default class MoveUpTune implements BlockTune {
|
|||
public render(): HTMLElement {
|
||||
const moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {});
|
||||
moveUpButton.appendChild($.svg('arrow-up', 14, 14));
|
||||
this.api.listeners.on(
|
||||
this.api.listeners.add(
|
||||
moveUpButton,
|
||||
'click',
|
||||
(event) => this.handleClick(event as MouseEvent, moveUpButton),
|
||||
|
|
|
@ -27,6 +27,7 @@ import MoveUpTune from './block-tunes/block-tune-move-up';
|
|||
import DeleteTune from './block-tunes/block-tune-delete';
|
||||
import MoveDownTune from './block-tunes/block-tune-move-down';
|
||||
import SelectionUtils from './selection';
|
||||
import {EventEmitter} from './eventEmitter';
|
||||
|
||||
/**
|
||||
* @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance
|
||||
|
@ -36,7 +37,7 @@ import SelectionUtils from './selection';
|
|||
* @property holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class
|
||||
* @property pluginsContent - HTML content that returns by Tool's render function
|
||||
*/
|
||||
export default class Block {
|
||||
export default class Block extends EventEmitter {
|
||||
|
||||
/**
|
||||
* CSS classes for the Block
|
||||
|
@ -326,6 +327,8 @@ export default class Block {
|
|||
settings: ToolConfig,
|
||||
apiMethods: API,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.name = toolName;
|
||||
this.tool = toolInstance;
|
||||
this.class = toolClass;
|
||||
|
@ -334,6 +337,7 @@ export default class Block {
|
|||
this.holder = this.compose();
|
||||
|
||||
this.mutationObserver = new MutationObserver(this.didMutated);
|
||||
this.holder.addEventListener('beforeinput', this.onInput);
|
||||
|
||||
/**
|
||||
* @type {BlockTune[]}
|
||||
|
@ -484,6 +488,83 @@ export default class Block {
|
|||
this.updateCurrentInput();
|
||||
}
|
||||
|
||||
private onInput = (e) => {
|
||||
let value = e.data;
|
||||
const selection = SelectionUtils.get();
|
||||
const range = SelectionUtils.range;
|
||||
const tempWrapper = $.make('div');
|
||||
const rangeContents = range.cloneContents();
|
||||
|
||||
tempWrapper.appendChild(rangeContents);
|
||||
|
||||
let html = tempWrapper.innerHTML;
|
||||
|
||||
const inputs = this.inputs;
|
||||
const inputIndex = inputs.findIndex((i) => i.contains(selection.anchorNode));
|
||||
|
||||
if (inputIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const input = inputs[inputIndex];
|
||||
const nodes = [];
|
||||
|
||||
const findNodes = (node: Node) => {
|
||||
if (node === input) {
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.push(Array.from(node.parentNode.childNodes).indexOf(node as ChildNode));
|
||||
|
||||
findNodes(node.parentNode as Node);
|
||||
};
|
||||
|
||||
findNodes(selection.anchorNode);
|
||||
|
||||
nodes.reverse();
|
||||
|
||||
switch (e.inputType) {
|
||||
case 'insertText':
|
||||
this.emit('text/insert', {
|
||||
position: {
|
||||
input: inputIndex,
|
||||
nodes,
|
||||
startOffset: range.startOffset,
|
||||
endOffset: range.startOffset + value.length,
|
||||
},
|
||||
data: {
|
||||
text: value,
|
||||
html,
|
||||
},
|
||||
});
|
||||
break;
|
||||
|
||||
case 'deleteContentBackward':
|
||||
value = tempWrapper.textContent;
|
||||
const {endOffset} = range;
|
||||
let {startOffset} = range;
|
||||
|
||||
if (!html.length) {
|
||||
html = selection.anchorNode.textContent[range.startOffset - 1];
|
||||
value = html;
|
||||
startOffset -= 1;
|
||||
}
|
||||
|
||||
this.emit('text/delete', {
|
||||
position: {
|
||||
input: inputIndex,
|
||||
startOffset,
|
||||
endOffset,
|
||||
nodes,
|
||||
},
|
||||
data: {
|
||||
text: value,
|
||||
html,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make default Block wrappers and put Tool`s content there
|
||||
* @returns {HTMLDivElement}
|
||||
|
|
|
@ -65,7 +65,7 @@ export default class Core {
|
|||
this.isReady = new Promise((resolve, reject) => {
|
||||
onReady = resolve;
|
||||
onFail = reject;
|
||||
});
|
||||
}) as Promise<void>;
|
||||
|
||||
Promise.resolve()
|
||||
.then(async () => {
|
||||
|
@ -249,6 +249,7 @@ export default class Core {
|
|||
'DragNDrop',
|
||||
'ModificationsObserver',
|
||||
'BlockSelection',
|
||||
'OTManager',
|
||||
];
|
||||
|
||||
await modulesToPrepare.reduce(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Module from '../__module';
|
||||
import Module from './__module';
|
||||
|
||||
/**
|
||||
* @module eventDispatcher
|
||||
|
@ -10,10 +10,10 @@ import Module from '../__module';
|
|||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @typedef {Events} Events
|
||||
* @typedef {EventEmitter} EventEmitter
|
||||
* @property {Object} subscribers - all subscribers grouped by event name
|
||||
*/
|
||||
export default class Events extends Module {
|
||||
export class EventEmitter {
|
||||
|
||||
/**
|
||||
* Object with events` names as key and array of callback functions as value
|
||||
|
@ -77,3 +77,5 @@ export default class Events extends Module {
|
|||
this.subscribers = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default new EventEmitter();
|
198
src/components/modules/OTManager.ts
Normal file
198
src/components/modules/OTManager.ts
Normal file
|
@ -0,0 +1,198 @@
|
|||
import Module from '../__module';
|
||||
import {BlockDeleteOperation, BlockInsertOperation} from '../ot/blocks';
|
||||
import Block from '../block';
|
||||
import Operation from '../ot/base';
|
||||
import {TextDeleteOperation, TextInsertOperation} from '../ot/text';
|
||||
import SelectionUtils from '../selection';
|
||||
import $ from '../dom';
|
||||
|
||||
export default class OTManager extends Module {
|
||||
public stack: Operation[] = [];
|
||||
|
||||
public prepare() {
|
||||
this.Editor.Renderer.on('render/finished', () => {
|
||||
this.Editor.BlockManager.on('block/insert', this.onBlockInsert);
|
||||
this.Editor.BlockManager.on('block/delete', this.onBlockDelete);
|
||||
this.Editor.BlockManager.on('text/insert', this.onTextInsert);
|
||||
this.Editor.BlockManager.on('text/delete', this.onTextDelete);
|
||||
});
|
||||
|
||||
this.Editor.Shortcuts.add({name: 'CMD+Z', handler: this.handleUndo});
|
||||
this.Editor.Shortcuts.add({name: 'CMD+Y', handler: this.handleRedo});
|
||||
}
|
||||
|
||||
public apply(operation: Operation) {
|
||||
const {BlockManager, Caret} = this.Editor;
|
||||
let range, input, node, selection;
|
||||
|
||||
switch (operation.type) {
|
||||
case BlockInsertOperation.TYPE:
|
||||
BlockManager.currentBlockIndex = operation.block - 1;
|
||||
BlockManager.insert(operation.data.tool, operation.data.data);
|
||||
Caret.setToBlock(BlockManager.currentBlock, Caret.positions.END);
|
||||
break;
|
||||
|
||||
case BlockDeleteOperation.TYPE:
|
||||
BlockManager.removeBlock(operation.block);
|
||||
Caret.setToBlock(BlockManager.blocks[operation.block - 1], Caret.positions.END);
|
||||
break;
|
||||
|
||||
case TextInsertOperation.TYPE:
|
||||
BlockManager.currentBlockIndex = operation.block;
|
||||
input = BlockManager.currentBlock.inputs[operation.input];
|
||||
|
||||
range = document.createRange();
|
||||
selection = SelectionUtils.get();
|
||||
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
||||
node = operation.nodes.reduce((anchorNode: Node, i: number) => {
|
||||
return anchorNode.childNodes[i];
|
||||
}, input);
|
||||
|
||||
range.setStart(node, operation.startOffset);
|
||||
|
||||
const tempWrapper = $.make('div');
|
||||
|
||||
tempWrapper.innerHTML = operation.data.value;
|
||||
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
tempWrapper.childNodes.forEach((child) => {
|
||||
fragment.appendChild(child);
|
||||
});
|
||||
|
||||
range.insertNode(fragment);
|
||||
input.normalize();
|
||||
range.collapse();
|
||||
break;
|
||||
|
||||
case TextDeleteOperation.TYPE:
|
||||
BlockManager.currentBlockIndex = operation.block;
|
||||
input = BlockManager.currentBlock.inputs[operation.input];
|
||||
|
||||
range = document.createRange();
|
||||
|
||||
selection = SelectionUtils.get();
|
||||
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
||||
node = operation.nodes.reduce((anchorNode: Node, i: number) => {
|
||||
return anchorNode.childNodes[i];
|
||||
}, input);
|
||||
|
||||
range.setStart(node, operation.startOffset);
|
||||
range.setEnd(node, operation.endOffset);
|
||||
|
||||
range.extractContents();
|
||||
}
|
||||
}
|
||||
|
||||
private handleUndo = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const op = this.stack.find((o) => !o.reversed && !o.canceled);
|
||||
|
||||
if (!op) {
|
||||
return;
|
||||
}
|
||||
|
||||
op.canceled = true;
|
||||
|
||||
const newOp = op.reverse();
|
||||
|
||||
this.apply(newOp);
|
||||
|
||||
if (newOp.needForceUpdate) {
|
||||
this.stack.unshift(newOp);
|
||||
} else {
|
||||
setImmediate(() => {
|
||||
this.stack[0].reversed = true;
|
||||
});
|
||||
}
|
||||
|
||||
console.log(this.stack);
|
||||
}
|
||||
|
||||
private handleRedo = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const op = this.stack.find((o) => o.reversed && !o.canceled);
|
||||
|
||||
if (!op) {
|
||||
return;
|
||||
}
|
||||
|
||||
op.canceled = true;
|
||||
|
||||
const newOp = op.reverse();
|
||||
|
||||
newOp.reversed = false;
|
||||
|
||||
this.apply(newOp);
|
||||
|
||||
if (newOp.needForceUpdate) {
|
||||
this.stack.unshift(newOp);
|
||||
}
|
||||
|
||||
console.log(this.stack);
|
||||
}
|
||||
|
||||
private onBlockInsert = async ({index, block}: {index: number, block: Block, data: any}) => {
|
||||
const operation = new BlockInsertOperation(index, block.name, await block.data);
|
||||
|
||||
this.stack.unshift(operation);
|
||||
}
|
||||
|
||||
private onBlockDelete = async ({index, block}: {index: number, block: Block}) => {
|
||||
const operation = new BlockDeleteOperation(index, block.name, await block.data);
|
||||
|
||||
this.stack.unshift(operation);
|
||||
}
|
||||
|
||||
private onTextInsert = ({position, data, block}) => {
|
||||
const operation = new TextInsertOperation(
|
||||
data.html,
|
||||
block,
|
||||
position.input,
|
||||
position.nodes,
|
||||
position.startOffset,
|
||||
position.endOffset,
|
||||
);
|
||||
|
||||
const prevOp = this.stack[0];
|
||||
|
||||
const prevOpIsMergable = prevOp && !prevOp.reversed && !prevOp.canceled && prevOp.mergeable;
|
||||
const newOpIsMergeable = operation.mergeable && !operation.reversed;
|
||||
const mergeable = prevOpIsMergable && newOpIsMergeable && prevOp.type === operation.type;
|
||||
|
||||
if (!mergeable || !(prevOp as TextInsertOperation).merge(operation)) {
|
||||
this.stack.unshift(operation);
|
||||
}
|
||||
}
|
||||
|
||||
private onTextDelete = ({data, block, position}) => {
|
||||
const operation = new TextDeleteOperation(
|
||||
data.html,
|
||||
block,
|
||||
position.input,
|
||||
position.nodes,
|
||||
position.startOffset,
|
||||
position.endOffset,
|
||||
);
|
||||
|
||||
const prevOp = this.stack[0];
|
||||
|
||||
const prevOpIsMergable = prevOp && !prevOp.reversed && !prevOp.canceled && prevOp.mergeable;
|
||||
const newOpIsMergeable = operation.mergeable && !operation.reversed;
|
||||
const mergeable = prevOpIsMergable && newOpIsMergeable && prevOp.type === operation.type;
|
||||
|
||||
console.log(prevOp, mergeable);
|
||||
|
||||
if (!mergeable || !(prevOp as TextDeleteOperation).merge(operation)) {
|
||||
this.stack.unshift(operation);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import Module from '../../__module';
|
||||
import {Events} from '../../../../types/api';
|
||||
|
||||
/**
|
||||
* @class EventsAPI
|
||||
* provides with methods working with Toolbar
|
||||
*/
|
||||
export default class EventsAPI extends Module {
|
||||
/**
|
||||
* Available methods
|
||||
* @return {Events}
|
||||
*/
|
||||
get methods(): Events {
|
||||
return {
|
||||
emit: (eventName: string, data: object) => this.emit(eventName, data),
|
||||
off: (eventName: string, callback: () => void) => this.off(eventName, callback),
|
||||
on: (eventName: string, callback: () => void) => this.on(eventName, callback),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe on Events
|
||||
* @param {String} eventName
|
||||
* @param {Function} callback
|
||||
*/
|
||||
public on(eventName, callback): void {
|
||||
this.Editor.Events.on(eventName, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit event with data
|
||||
* @param {String} eventName
|
||||
* @param {Object} data
|
||||
*/
|
||||
public emit(eventName, data): void {
|
||||
this.Editor.Events.emit(eventName, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from Event
|
||||
* @param {String} eventName
|
||||
* @param {Function} callback
|
||||
*/
|
||||
public off(eventName, callback): void {
|
||||
this.Editor.Events.off(eventName, callback);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,7 +16,6 @@ export default class API extends Module {
|
|||
return {
|
||||
blocks: this.Editor.BlocksAPI.methods,
|
||||
caret: this.Editor.CaretAPI.methods,
|
||||
events: this.Editor.EventsAPI.methods,
|
||||
listeners: this.Editor.ListenersAPI.methods,
|
||||
notifier: this.Editor.NotifierAPI.methods,
|
||||
sanitizer: this.Editor.SanitizerAPI.methods,
|
||||
|
|
|
@ -12,9 +12,9 @@ export default class ListenersAPI extends Module {
|
|||
*/
|
||||
get methods(): Listeners {
|
||||
return {
|
||||
on: (element: HTMLElement, eventType, handler, useCapture) => this.on(element, eventType, handler, useCapture),
|
||||
off: (element, eventType, handler) => this.off(element, eventType, handler),
|
||||
};
|
||||
add: (element: HTMLElement, eventType, handler, useCapture) => this.add(element, eventType, handler, useCapture),
|
||||
remove: (element, eventType, handler) => this.remove(element, eventType, handler),
|
||||
} as Listeners;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,8 +25,8 @@ export default class ListenersAPI extends Module {
|
|||
* @param {() => void} handler
|
||||
* @param {boolean} useCapture
|
||||
*/
|
||||
public on(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void {
|
||||
this.Editor.Listeners.on(element, eventType, handler, useCapture);
|
||||
public add(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void {
|
||||
this.Editor.Listeners.add(element, eventType, handler, useCapture);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +36,7 @@ export default class ListenersAPI extends Module {
|
|||
* @param eventType
|
||||
* @param handler
|
||||
*/
|
||||
public off(element, eventType, handler): void {
|
||||
this.Editor.Listeners.off(element, eventType, handler);
|
||||
public remove(element, eventType, handler): void {
|
||||
this.Editor.Listeners.remove(element, eventType, handler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export default class SaverAPI extends Module {
|
|||
get methods(): Saver {
|
||||
return {
|
||||
save: () => this.save(),
|
||||
};
|
||||
} as Saver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,7 +14,9 @@ export default class ToolbarAPI extends Module {
|
|||
return {
|
||||
close: () => this.close(),
|
||||
open: () => this.open(),
|
||||
};
|
||||
on: (event: string, callback: (data: any) => void) => this.Editor.Toolbar.on(event, callback),
|
||||
off: (event: string, callback: (data: any) => void) => this.Editor.Toolbar.off(event, callback),
|
||||
} as Toolbar;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -195,6 +195,9 @@ export default class BlockManager extends Module {
|
|||
const toolClass = this.Editor.Tools.available[toolName] as BlockToolConstructable;
|
||||
const block = new Block(toolName, toolInstance, toolClass, settings, this.Editor.API.methods);
|
||||
|
||||
block.on('text/insert', (d) => this.emit('text/insert', {...d, block: this.currentBlockIndex}));
|
||||
block.on('text/delete', (d) => this.emit('text/delete', {...d, block: this.currentBlockIndex}));
|
||||
|
||||
this.bindEvents(block);
|
||||
|
||||
return block;
|
||||
|
@ -220,6 +223,13 @@ export default class BlockManager extends Module {
|
|||
const block = this.composeBlock(toolName, data, settings);
|
||||
|
||||
this._blocks[newIndex] = block;
|
||||
|
||||
this.emit('block/insert', {
|
||||
block,
|
||||
index: newIndex,
|
||||
data,
|
||||
});
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -270,6 +280,12 @@ export default class BlockManager extends Module {
|
|||
this.currentBlockIndex++;
|
||||
}
|
||||
|
||||
this.emit('block/insert', {
|
||||
block,
|
||||
index,
|
||||
data: {},
|
||||
});
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -321,6 +337,12 @@ export default class BlockManager extends Module {
|
|||
if (index === undefined) {
|
||||
index = this.currentBlockIndex;
|
||||
}
|
||||
|
||||
this.emit('block/delete', {
|
||||
block: this.blocks[index],
|
||||
index,
|
||||
});
|
||||
|
||||
this._blocks.remove(index);
|
||||
|
||||
if (this.currentBlockIndex >= index) {
|
||||
|
@ -397,6 +419,12 @@ export default class BlockManager extends Module {
|
|||
|
||||
this._blocks.insert(this.currentBlockIndex, block, true);
|
||||
|
||||
this.emit('block/insert', {
|
||||
block,
|
||||
index: this.currentBlockIndex,
|
||||
data,
|
||||
});
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
|
@ -544,10 +572,10 @@ export default class BlockManager extends Module {
|
|||
private bindEvents(block: Block): void {
|
||||
const {BlockEvents, Listeners} = this.Editor;
|
||||
|
||||
Listeners.on(block.holder, 'keydown', (event) => BlockEvents.keydown(event as KeyboardEvent), true);
|
||||
Listeners.on(block.holder, 'mouseup', (event) => BlockEvents.mouseUp(event));
|
||||
Listeners.on(block.holder, 'keyup', (event) => BlockEvents.keyup(event));
|
||||
Listeners.on(block.holder, 'dragover', (event) => BlockEvents.dragOver(event as DragEvent));
|
||||
Listeners.on(block.holder, 'dragleave', (event) => BlockEvents.dragLeave(event as DragEvent));
|
||||
Listeners.add(block.holder, 'keydown', (event) => BlockEvents.keydown(event as KeyboardEvent), true);
|
||||
Listeners.add(block.holder, 'mouseup', (event) => BlockEvents.mouseUp(event));
|
||||
Listeners.add(block.holder, 'keyup', (event) => BlockEvents.keyup(event));
|
||||
Listeners.add(block.holder, 'dragover', (event) => BlockEvents.dragOver(event as DragEvent));
|
||||
Listeners.add(block.holder, 'dragleave', (event) => BlockEvents.dragLeave(event as DragEvent));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,9 +25,9 @@ export default class DragNDrop extends Module {
|
|||
* @private
|
||||
*/
|
||||
private bindEvents(): void {
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true);
|
||||
this.Editor.Listeners.add(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true);
|
||||
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragstart', (dragEvent: DragEvent) => {
|
||||
this.Editor.Listeners.add(this.Editor.UI.nodes.holder, 'dragstart', (dragEvent: DragEvent) => {
|
||||
|
||||
if (SelectionUtils.isAtEditor && !SelectionUtils.isCollapsed) {
|
||||
this.isStartedAtEditor = true;
|
||||
|
@ -37,7 +37,7 @@ export default class DragNDrop extends Module {
|
|||
});
|
||||
|
||||
/* Prevent default browser behavior to allow drop on non-contenteditable elements */
|
||||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragover', (e) => e.preventDefault(), true);
|
||||
this.Editor.Listeners.add(this.Editor.UI.nodes.holder, 'dragover', (e) => e.preventDefault(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,7 +59,7 @@ export default class Listeners extends Module {
|
|||
* @param {Function} handler - method that will be fired on event
|
||||
* @param {Boolean} useCapture - use event bubbling
|
||||
*/
|
||||
public on(
|
||||
public add(
|
||||
element: EventTarget,
|
||||
eventType: string,
|
||||
handler: (event: Event) => void,
|
||||
|
@ -88,7 +88,7 @@ export default class Listeners extends Module {
|
|||
* @param {Function} handler - remove handler, if element listens several handlers on the same event type
|
||||
* @param {Boolean} useCapture - use event bubbling
|
||||
*/
|
||||
public off(
|
||||
public remove(
|
||||
element: EventTarget,
|
||||
eventType: string,
|
||||
handler: (event: Event) => void,
|
||||
|
|
|
@ -179,7 +179,7 @@ export default class Paste extends Module {
|
|||
private setCallback(): void {
|
||||
const {Listeners, UI} = this.Editor;
|
||||
|
||||
Listeners.on(document, 'paste', this.handlePasteEvent);
|
||||
Listeners.add(document, 'paste', this.handlePasteEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,10 +42,14 @@ export default class Renderer extends Module {
|
|||
* Make plugin blocks from array of plugin`s data
|
||||
* @param {RendererBlocks[]} blocks
|
||||
*/
|
||||
public render(blocks: BlockToolData[]): Promise<void> {
|
||||
public async render(blocks: BlockToolData[]): Promise<void> {
|
||||
this.emit('render/started');
|
||||
|
||||
const chainData = blocks.map((block) => ({function: () => this.insertBlock(block)}));
|
||||
|
||||
return _.sequence(chainData as ChainData[]);
|
||||
await _.sequence(chainData as ChainData[]);
|
||||
|
||||
this.emit('render/finished');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,6 +30,7 @@ export default class Saver extends Module {
|
|||
const blocks = BlockManager.blocks,
|
||||
chainData = [];
|
||||
|
||||
this.emit('saving/started');
|
||||
/**
|
||||
* Disable modifications observe while saving
|
||||
*/
|
||||
|
@ -44,7 +45,11 @@ export default class Saver extends Module {
|
|||
|
||||
ModificationsObserver.enable();
|
||||
|
||||
return this.makeOutput(sanitizedData);
|
||||
const output = this.makeOutput(sanitizedData);
|
||||
|
||||
this.emit('saving/finished', output);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,8 +20,8 @@ export default class BlockSettings extends Module {
|
|||
*/
|
||||
public get events(): {opened: string, closed: string} {
|
||||
return {
|
||||
opened: 'block-settings-opened',
|
||||
closed: 'block-settings-closed',
|
||||
opened: 'block-settings/opened',
|
||||
closed: 'block-settings/closed',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ export default class BlockSettings extends Module {
|
|||
this.addDefaultSettings();
|
||||
|
||||
/** Tell to subscribers that block settings is opened */
|
||||
this.Editor.Events.emit(this.events.opened);
|
||||
this.emit(this.events.opened);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +105,7 @@ export default class BlockSettings extends Module {
|
|||
this.nodes.defaultSettings.innerHTML = '';
|
||||
|
||||
/** Tell to subscribers that block settings is closed */
|
||||
this.Editor.Events.emit(this.events.closed);
|
||||
this.emit(this.events.closed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -114,7 +114,7 @@ export default class Toolbar extends Module {
|
|||
this.nodes.plusButton = $.make('div', Toolbar.CSS.plusButton);
|
||||
$.append(this.nodes.plusButton, $.svg('plus', 14, 14));
|
||||
$.append(this.nodes.content, this.nodes.plusButton);
|
||||
this.Editor.Listeners.on(this.nodes.plusButton, 'click', () => this.plusButtonClicked(), false);
|
||||
this.Editor.Listeners.add(this.nodes.plusButton, 'click', () => this.plusButtonClicked(), false);
|
||||
|
||||
/**
|
||||
* Make a Toolbox
|
||||
|
@ -275,7 +275,7 @@ export default class Toolbar extends Module {
|
|||
/**
|
||||
* Settings toggler
|
||||
*/
|
||||
this.Editor.Listeners.on(this.nodes.settingsToggler, 'click', () => this.settingsTogglerClicked());
|
||||
this.Editor.Listeners.add(this.nodes.settingsToggler, 'click', () => this.settingsTogglerClicked());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -110,7 +110,7 @@ export default class InlineToolbar extends Module {
|
|||
this.nodes.actions = $.make('div', this.CSS.actionsWrapper);
|
||||
|
||||
// To prevent reset of a selection when click on the wrapper
|
||||
this.Editor.Listeners.on(this.nodes.wrapper, 'mousedown', (event) => {
|
||||
this.Editor.Listeners.add(this.nodes.wrapper, 'mousedown', (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
|
@ -342,7 +342,7 @@ export default class InlineToolbar extends Module {
|
|||
this.nodes.actions.appendChild(actions);
|
||||
}
|
||||
|
||||
Listeners.on(button, 'click', (event) => {
|
||||
Listeners.add(button, 'click', (event) => {
|
||||
this.toolClicked(tool);
|
||||
event.preventDefault();
|
||||
});
|
||||
|
|
|
@ -268,7 +268,7 @@ export default class Toolbox extends Module {
|
|||
/**
|
||||
* Add click listener
|
||||
*/
|
||||
this.Editor.Listeners.on(button, 'click', (event: KeyboardEvent|MouseEvent) => {
|
||||
this.Editor.Listeners.add(button, 'click', (event: KeyboardEvent|MouseEvent) => {
|
||||
this.toolButtonActivate(event, toolName);
|
||||
});
|
||||
|
||||
|
|
|
@ -183,14 +183,14 @@ export default class UI extends Module {
|
|||
* Bind events on the CodeX Editor interface
|
||||
*/
|
||||
private bindEvents(): void {
|
||||
this.Editor.Listeners.on(
|
||||
this.Editor.Listeners.add(
|
||||
this.nodes.redactor,
|
||||
'click',
|
||||
(event) => this.redactorClicked(event as MouseEvent),
|
||||
false,
|
||||
);
|
||||
this.Editor.Listeners.on(document, 'keydown', (event) => this.documentKeydown(event as KeyboardEvent), true );
|
||||
this.Editor.Listeners.on(document, 'click', (event) => this.documentClicked(event as MouseEvent), false );
|
||||
this.Editor.Listeners.add(document, 'keydown', (event) => this.documentKeydown(event as KeyboardEvent), true );
|
||||
this.Editor.Listeners.add(document, 'click', (event) => this.documentClicked(event as MouseEvent), false );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
50
src/components/ot/base.ts
Normal file
50
src/components/ot/base.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
export interface OperationData {
|
||||
type: string;
|
||||
block: number;
|
||||
input?: number;
|
||||
nodes?: number[];
|
||||
startOffset?: number;
|
||||
endOffset?: number;
|
||||
data: any;
|
||||
}
|
||||
|
||||
export default abstract class Operation {
|
||||
|
||||
public type: string;
|
||||
public block: number;
|
||||
public input?: number;
|
||||
public nodes?: number[];
|
||||
public startOffset?: number;
|
||||
public endOffset?: number;
|
||||
public data: any;
|
||||
public reversed: boolean = false;
|
||||
public canceled: boolean = false;
|
||||
public needForceUpdate?: boolean = false;
|
||||
public mergeable?: boolean = false;
|
||||
|
||||
protected constructor(opData: OperationData) {
|
||||
this.type = opData.type;
|
||||
this.block = opData.block;
|
||||
this.input = opData.input;
|
||||
this.nodes = opData.nodes;
|
||||
this.startOffset = opData.startOffset;
|
||||
this.endOffset = opData.endOffset;
|
||||
this.data = opData.data;
|
||||
}
|
||||
|
||||
public abstract transform(opertaion: Operation): Operation;
|
||||
|
||||
public abstract reverse(): Operation;
|
||||
|
||||
protected clone(): Operation {
|
||||
return new (this as any).constructor({
|
||||
type: this.type,
|
||||
block: this.block,
|
||||
input: this.input,
|
||||
nodes: this.nodes,
|
||||
startOffset: this.startOffset,
|
||||
endOffset: this.endOffset,
|
||||
data: Object.assign({}, this.data),
|
||||
});
|
||||
}
|
||||
}
|
43
src/components/ot/blocks/delete.ts
Normal file
43
src/components/ot/blocks/delete.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import Operation from '../base';
|
||||
import {BlockInsertOperation} from './index';
|
||||
|
||||
export class BlockDeleteOperation extends Operation {
|
||||
public static TYPE = 'block/delete';
|
||||
|
||||
constructor(index: number, tool: string, data: any) {
|
||||
super({
|
||||
type: BlockDeleteOperation.TYPE,
|
||||
block: index,
|
||||
data: {
|
||||
tool,
|
||||
data,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public transform(operation: Operation): Operation {
|
||||
const clone = this.clone();
|
||||
|
||||
switch (operation.type) {
|
||||
case BlockInsertOperation.TYPE:
|
||||
if (operation.block <= clone.block) {
|
||||
clone.block++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlockDeleteOperation.TYPE:
|
||||
if (operation.block <= clone.block) {
|
||||
clone.block--;
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public reverse(): Operation {
|
||||
const op = new BlockInsertOperation(this.block, this.data.tool, this.data.data);
|
||||
|
||||
op.reversed = true;
|
||||
return op;
|
||||
}
|
||||
}
|
2
src/components/ot/blocks/index.ts
Normal file
2
src/components/ot/blocks/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './delete';
|
||||
export * from './insert';
|
43
src/components/ot/blocks/insert.ts
Normal file
43
src/components/ot/blocks/insert.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import Operation from '../base';
|
||||
import { BlockDeleteOperation } from './index';
|
||||
|
||||
export class BlockInsertOperation extends Operation {
|
||||
public static TYPE = 'block/insert';
|
||||
|
||||
constructor(index: number, tool: string, data: any) {
|
||||
super({
|
||||
type: BlockInsertOperation.TYPE,
|
||||
block: index,
|
||||
data: {
|
||||
tool,
|
||||
data,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public transform(operation: Operation): Operation {
|
||||
const newOp = this.clone();
|
||||
|
||||
switch (operation.type) {
|
||||
case BlockInsertOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlockDeleteOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block--;
|
||||
}
|
||||
}
|
||||
|
||||
return newOp;
|
||||
}
|
||||
|
||||
public reverse(): Operation {
|
||||
const op = new BlockDeleteOperation(this.block, this.data.tool, this.data.data);
|
||||
|
||||
op.reversed = true;
|
||||
return op;
|
||||
}
|
||||
}
|
101
src/components/ot/text/delete.ts
Normal file
101
src/components/ot/text/delete.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import Operation from '../base';
|
||||
import { BlockDeleteOperation, BlockInsertOperation } from '../blocks';
|
||||
import {TextInsertOperation} from './index';
|
||||
|
||||
export class TextDeleteOperation extends Operation {
|
||||
public static TYPE = 'text/delete';
|
||||
public needForceUpdate = true;
|
||||
public mergeable = true;
|
||||
|
||||
constructor(value: string, block: number, input: number, nodes: number[], startOffset: number, endOffset: number) {
|
||||
super({
|
||||
type: TextDeleteOperation.TYPE,
|
||||
block,
|
||||
input,
|
||||
nodes,
|
||||
startOffset,
|
||||
endOffset,
|
||||
data: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public transform(operation: Operation): Operation {
|
||||
const newOp = this.clone();
|
||||
|
||||
switch (operation.type) {
|
||||
case BlockInsertOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlockDeleteOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block--;
|
||||
}
|
||||
break;
|
||||
|
||||
case TextInsertOperation.TYPE:
|
||||
if (operation.startOffset <= newOp.startOffset) {
|
||||
const length = operation.endOffset - operation.startOffset;
|
||||
|
||||
newOp.startOffset += length;
|
||||
newOp.endOffset += length;
|
||||
}
|
||||
break;
|
||||
|
||||
case TextDeleteOperation.TYPE:
|
||||
if (operation.startOffset <= newOp.startOffset) {
|
||||
const length = operation.endOffset - operation.startOffset;
|
||||
|
||||
newOp.startOffset -= length;
|
||||
newOp.endOffset -= length;
|
||||
}
|
||||
}
|
||||
|
||||
return newOp;
|
||||
}
|
||||
|
||||
public reverse(): Operation {
|
||||
const op = new TextInsertOperation(
|
||||
this.data.value,
|
||||
this.block,
|
||||
this.input,
|
||||
this.nodes,
|
||||
this.startOffset,
|
||||
this.endOffset,
|
||||
);
|
||||
|
||||
op.reversed = true;
|
||||
return op;
|
||||
}
|
||||
|
||||
public merge(operation: TextDeleteOperation): boolean {
|
||||
if (this.block !== operation.block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.input !== operation.input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.nodes.length !== operation.nodes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.nodes.every((e, i) => operation.nodes[i] === e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.startOffset !== operation.endOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.data.value = operation.data.value + this.data.value;
|
||||
this.startOffset = operation.startOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
2
src/components/ot/text/index.ts
Normal file
2
src/components/ot/text/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './insert';
|
||||
export * from './delete';
|
101
src/components/ot/text/insert.ts
Normal file
101
src/components/ot/text/insert.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import Operation from '../base';
|
||||
import { BlockDeleteOperation, BlockInsertOperation } from '../blocks';
|
||||
import {TextDeleteOperation} from './index';
|
||||
|
||||
export class TextInsertOperation extends Operation {
|
||||
public static TYPE = 'text/insert';
|
||||
public needForceUpdate = true;
|
||||
public mergeable = true;
|
||||
|
||||
constructor(value: string, block: number, input: number, nodes: number[], startOffset: number, endOffset: number) {
|
||||
super({
|
||||
type: TextInsertOperation.TYPE,
|
||||
block,
|
||||
input,
|
||||
nodes,
|
||||
startOffset,
|
||||
endOffset,
|
||||
data: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
public transform(operation: Operation): Operation {
|
||||
const newOp = this.clone();
|
||||
|
||||
switch (operation.type) {
|
||||
case BlockInsertOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block++;
|
||||
}
|
||||
break;
|
||||
|
||||
case BlockDeleteOperation.TYPE:
|
||||
if (operation.block <= newOp.block) {
|
||||
newOp.block--;
|
||||
}
|
||||
break;
|
||||
|
||||
case TextInsertOperation.TYPE:
|
||||
if (operation.startOffset <= newOp.startOffset) {
|
||||
const length = operation.endOffset - operation.startOffset;
|
||||
|
||||
newOp.startOffset += length;
|
||||
newOp.endOffset += length;
|
||||
}
|
||||
break;
|
||||
|
||||
case TextDeleteOperation.TYPE:
|
||||
if (operation.startOffset <= newOp.startOffset) {
|
||||
const length = operation.endOffset - operation.startOffset;
|
||||
|
||||
newOp.startOffset -= length;
|
||||
newOp.endOffset -= length;
|
||||
}
|
||||
}
|
||||
|
||||
return newOp;
|
||||
}
|
||||
|
||||
public reverse(): Operation {
|
||||
const op = new TextDeleteOperation(
|
||||
this.data.value,
|
||||
this.block,
|
||||
this.input,
|
||||
this.nodes,
|
||||
this.startOffset,
|
||||
this.endOffset,
|
||||
);
|
||||
|
||||
op.reversed = true;
|
||||
return op;
|
||||
}
|
||||
|
||||
public merge(operation: TextInsertOperation): boolean {
|
||||
if (this.block !== operation.block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.input !== operation.input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.nodes.length !== operation.nodes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.nodes.every((e, i) => operation.nodes[i] === e)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.endOffset !== operation.startOffset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.data.value += operation.data.value;
|
||||
this.endOffset = operation.endOffset;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
7
src/types-internal/editor-modules.d.ts
vendored
7
src/types-internal/editor-modules.d.ts
vendored
|
@ -5,7 +5,7 @@ import Toolbar from '../components/modules/toolbar/index';
|
|||
import InlineToolbar from '../components/modules/toolbar/inline';
|
||||
import Toolbox from '../components/modules/toolbar/toolbox';
|
||||
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
||||
import Events from '../components/modules/events';
|
||||
import {EventEmitter} from '../components/eventEmitter';
|
||||
import Shortcuts from '../components/modules/shortcuts';
|
||||
import Paste from '../components/modules/paste';
|
||||
import Notifier from '../components/modules/notifier';
|
||||
|
@ -19,7 +19,6 @@ import Caret from '../components/modules/caret';
|
|||
import BlockManager from '../components/modules/blockManager';
|
||||
import BlocksAPI from '../components/modules/api/blocks';
|
||||
import CaretAPI from '../components/modules/api/caret';
|
||||
import EventsAPI from '../components/modules/api/events';
|
||||
import ListenersAPI from '../components/modules/api/listeners';
|
||||
import SanitizerAPI from '../components/modules/api/sanitizer';
|
||||
import ToolbarAPI from '../components/modules/api/toolbar';
|
||||
|
@ -29,6 +28,7 @@ import NotifierAPI from '../components/modules/api/notifier';
|
|||
import SaverAPI from '../components/modules/api/saver';
|
||||
import Saver from '../components/modules/saver';
|
||||
import BlockSelection from '../components/modules/blockSelection';
|
||||
import OTManager from '../components/modules/OTManager';
|
||||
|
||||
export interface EditorModules {
|
||||
UI: UI;
|
||||
|
@ -39,7 +39,6 @@ export interface EditorModules {
|
|||
InlineToolbar: InlineToolbar;
|
||||
Toolbox: Toolbox;
|
||||
BlockSettings: BlockSettings;
|
||||
Events: Events;
|
||||
Shortcuts: Shortcuts;
|
||||
Paste: Paste;
|
||||
DragNDrop: DragNDrop;
|
||||
|
@ -54,7 +53,6 @@ export interface EditorModules {
|
|||
BlockManager: BlockManager;
|
||||
BlocksAPI: BlocksAPI;
|
||||
CaretAPI: CaretAPI;
|
||||
EventsAPI: EventsAPI;
|
||||
ListenersAPI: ListenersAPI;
|
||||
SanitizerAPI: SanitizerAPI;
|
||||
SaverAPI: SaverAPI;
|
||||
|
@ -62,4 +60,5 @@ export interface EditorModules {
|
|||
StylesAPI: StylesAPI;
|
||||
ToolbarAPI: ToolbarAPI;
|
||||
NotifierAPI: NotifierAPI;
|
||||
OTManager: OTManager;
|
||||
}
|
||||
|
|
28
types/api/events.d.ts
vendored
28
types/api/events.d.ts
vendored
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Describes Editor`s events API
|
||||
*/
|
||||
export interface Events {
|
||||
/**
|
||||
* Emits event
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {any} data
|
||||
*/
|
||||
emit(eventName: string, data: any): void;
|
||||
|
||||
/**
|
||||
* Unsubscribe from event
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {(data: any) => void} callback
|
||||
*/
|
||||
off(eventName: string, callback: (data?: any) => void): void;
|
||||
|
||||
/**
|
||||
* Subscribe to event
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {(data: any) => void} callback
|
||||
*/
|
||||
on(eventName: string, callback: (data?: any) => void): void;
|
||||
}
|
1
types/api/index.d.ts
vendored
1
types/api/index.d.ts
vendored
|
@ -1,5 +1,4 @@
|
|||
export * from './blocks';
|
||||
export * from './events';
|
||||
export * from './listeners';
|
||||
export * from './sanitizer';
|
||||
export * from './saver';
|
||||
|
|
4
types/api/listeners.d.ts
vendored
4
types/api/listeners.d.ts
vendored
|
@ -10,7 +10,7 @@ export interface Listeners {
|
|||
* @param {(event: Event) => void}handler
|
||||
* @param {boolean} useCapture
|
||||
*/
|
||||
on(element: Element, eventType: string, handler: (event?: Event) => void, useCapture?: boolean): void;
|
||||
add(element: Element, eventType: string, handler: (event?: Event) => void, useCapture?: boolean): void;
|
||||
|
||||
/**
|
||||
* Unsubscribe from event dispatched on passed element
|
||||
|
@ -20,5 +20,5 @@ export interface Listeners {
|
|||
* @param {(event: Event) => void}handler
|
||||
* @param {boolean} useCapture
|
||||
*/
|
||||
off(element: Element, eventType: string, handler: (event?: Event) => void, useCapture?: boolean): void;
|
||||
remove(element: Element, eventType: string, handler: (event?: Event) => void, useCapture?: boolean): void;
|
||||
}
|
||||
|
|
3
types/api/toolbar.d.ts
vendored
3
types/api/toolbar.d.ts
vendored
|
@ -11,4 +11,7 @@ export interface Toolbar {
|
|||
* Opens Toolbar
|
||||
*/
|
||||
open(): void;
|
||||
|
||||
on(event: string, callback: (data: any) => void);
|
||||
off(event: string, callback: (data: any) => void);
|
||||
}
|
||||
|
|
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import {EditorConfig} from './configs';
|
||||
import {Blocks, Caret, Events, Listeners, Notifier, Sanitizer, Saver, Selection, Styles, Toolbar} from './api';
|
||||
import {Blocks, Caret, Listeners, Notifier, Sanitizer, Saver, Selection, Styles, Toolbar} from './api';
|
||||
|
||||
/**
|
||||
* Interfaces used for development
|
||||
|
|
Loading…
Reference in a new issue