diff --git a/addon/utils/ce/pernet-raw-editor.ts b/addon/utils/ce/pernet-raw-editor.ts index 4713a3962..aa2b19c60 100644 --- a/addon/utils/ce/pernet-raw-editor.ts +++ b/addon/utils/ce/pernet-raw-editor.ts @@ -41,6 +41,7 @@ import RichNode from "@lblod/marawa/rich-node"; import { tracked } from '@glimmer/tracking'; import { Editor } from "@lblod/ember-rdfa-editor/editor/input-handlers/manipulation"; import {ModelError} from "@lblod/ember-rdfa-editor/utils/errors"; +import EventBus from "@lblod/ember-rdfa-editor/utils/event-bus"; export interface ContentObserver { handleTextInsert: (position: number, text: string, extraInfo: Array) => void @@ -255,6 +256,7 @@ export default class PernetRawEditor extends RawEditor implements Editor { observer.handleFullContentUpdate(extraInfo); } } + EventBus.emit("contentChanged", undefined); } /** diff --git a/addon/utils/ce/raw-editor.ts b/addon/utils/ce/raw-editor.ts index 9b2b15c76..6c34f8e40 100644 --- a/addon/utils/ce/raw-editor.ts +++ b/addon/utils/ce/raw-editor.ts @@ -29,13 +29,14 @@ import Model from "@lblod/ember-rdfa-editor/model/model"; import ModelRange from '@lblod/ember-rdfa-editor/model/model-range'; import ModelSelection from '@lblod/ember-rdfa-editor/model/model-selection'; import ModelSelectionTracker from "@lblod/ember-rdfa-editor/utils/ce/model-selection-tracker"; -import { walk as walkDomNode } from "@lblod/marawa/node-walker"; +import {walk as walkDomNode} from "@lblod/marawa/node-walker"; import RichNode from "@lblod/marawa/rich-node"; import classic from 'ember-classic-decorator'; import ModelElement from "@lblod/ember-rdfa-editor/model/model-element"; import InsertXmlCommand from "@lblod/ember-rdfa-editor/commands/insert-xml-command"; import {ModelError} from "@lblod/ember-rdfa-editor/utils/errors"; import InsertTextCommand from "@lblod/ember-rdfa-editor/commands/insert-text-command"; +import EventBus, {EditorEventListener, EditorEventName} from "@lblod/ember-rdfa-editor/utils/event-bus"; /** * Raw contenteditable editor. This acts as both the internal and external API to the DOM. @@ -145,7 +146,7 @@ class RawEditor extends EmberObject { } get model(): Model { - if(!this._model) { + if (!this._model) { throw new ModelError("Model accessed before initialization is complete"); } return this._model; @@ -154,6 +155,7 @@ class RawEditor extends EmberObject { set model(value: Model) { this._model = value; } + /** * Register a command for use with {@link executeCommand} * @param command @@ -216,6 +218,14 @@ class RawEditor extends EmberObject { createSelection(): ModelSelection { return new ModelSelection(this.model); } + + on(eventName: E, callback: EditorEventListener) { + EventBus.on(eventName, callback); + } + + off(eventName: E, callback: EditorEventListener) { + EventBus.off(eventName, callback); + } } export default RawEditor; diff --git a/addon/utils/event-bus.ts b/addon/utils/event-bus.ts new file mode 100644 index 000000000..e78df1f36 --- /dev/null +++ b/addon/utils/event-bus.ts @@ -0,0 +1,68 @@ +import {createLogger, Logger} from "@lblod/ember-rdfa-editor/utils/logging-utils"; + +export interface EditorEvent { + name: E, + payload: EDITOR_EVENT_MAP[E] +} + +export type EDITOR_EVENT_MAP = { + "contentChanged": void +}; +export type EditorEventName = keyof EDITOR_EVENT_MAP; + +export type EditorEventListener = (event: EditorEvent) => void; + +export default class EventBus { + static instance: EventBus; + + private static getInstance() { + if (!this.instance) { + this.instance = new EventBus(); + } + return this.instance; + } + + static on(eventName: E, callback: EditorEventListener): void { + this.getInstance().on(eventName, callback); + } + + static off(eventName: E, callback: EditorEventListener): void { + this.getInstance().off(eventName, callback); + } + + // TODO: figure out how to allow void events to omit the payload argument + static emit(eventName: E, payload: EDITOR_EVENT_MAP[E]): void { + this.getInstance().emit(eventName, payload); + } + + private listeners: Map>> = new Map>>(); + private logger: Logger = createLogger("EventBus"); + + private on(eventName: E, callback: EditorEventListener): void { + const eventListeners = this.listeners.get(eventName); + if (eventListeners) { + eventListeners.push(callback); + } else { + this.listeners.set(eventName, [callback]); + } + } + + private off(eventName: E, callback: EditorEventListener): void { + const eventListeners = this.listeners.get(eventName); + if (eventListeners) { + const index = eventListeners.indexOf(callback); + if (index !== -1) { + eventListeners.splice(index, 1); + } + } + + } + + private emit(eventName: E, payload: EDITOR_EVENT_MAP[E]): void { + const eventListeners = this.listeners.get(eventName); + this.logger.log(`Emitting event: ${eventName} with payload:`, payload); + if (eventListeners) { + eventListeners.forEach(listener => listener({name: eventName, payload})); + } + } +} diff --git a/addon/utils/rdfa/rdfa-document.ts b/addon/utils/rdfa/rdfa-document.ts index ca0814655..d22e93620 100644 --- a/addon/utils/rdfa/rdfa-document.ts +++ b/addon/utils/rdfa/rdfa-document.ts @@ -2,6 +2,7 @@ import PernetRawEditor from '../ce/pernet-raw-editor'; import xmlFormat from 'xml-formatter'; import HTMLExportWriter from '@lblod/ember-rdfa-editor/model/writers/html-export-writer'; import ModelRange from '@lblod/ember-rdfa-editor/model/model-range'; +import EventBus, {EditorEventListener} from "@lblod/ember-rdfa-editor/utils/event-bus"; /** * RdfaDocument is a virtual representation of the document @@ -56,8 +57,16 @@ export default class RdfaDocument { return result; } - setHtmlContent(html: string) { this.htmlContent = html; } + + // this shows how we can limit the public events with types + on(eventName: E, callback: EditorEventListener) { + EventBus.on(eventName, callback); + } + + off(eventName: E, callback: EditorEventListener) { + EventBus.off(eventName, callback); + } }