import * as EventUtils from '../../events/EventUtils'; import EventDispatcher, { EditorEvent, MappedEvent } from './EventDispatcher'; interface Observable { fire >(name: K, args?: U, bubble?: boolean): EditorEvent; dispatch >(name: K, args?: U, bubble?: boolean): EditorEvent; on (name: K, callback: (event: EditorEvent>) => void, prepend?: boolean): EventDispatcher; off (name?: K, callback?: (event: EditorEvent>) => void): EventDispatcher; once (name: K, callback: (event: EditorEvent>) => void): EventDispatcher; hasEventListeners (name: string): boolean; } interface ParentObservable extends Observable { parent?: () => ParentObservable; } interface ObservableObject { _eventDispatcher?: EventDispatcher; toggleNativeEvent?: (name: string, state: boolean) => void; removed?: boolean; parent?: () => ParentObservable | undefined; } /** * This mixin adds event binding logic to classes. Adapts the EventDispatcher class. * * @mixin tinymce.util.Observable */ const getEventDispatcher = (obj: ObservableObject): EventDispatcher => { if (!obj._eventDispatcher) { obj._eventDispatcher = new EventDispatcher({ scope: obj, toggleEvent: (name: string, state: boolean) => { if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) { obj.toggleNativeEvent(name, state); } } }); } return obj._eventDispatcher; }; const Observable: Observable = { /** * Fires the specified event by name. Consult the * event reference for more details on each event. *
* Deprecated in TinyMCE 6.0 and has been marked for removal in TinyMCE 7.0. Use dispatch instead. * * @method fire * @param {String} name Name of the event to fire. * @param {Object?} args Event arguments. * @param {Boolean?} bubble True/false if the event is to be bubbled. * @return {Object} Event args instance passed in. * @deprecated Use dispatch() instead * @example * instance.fire('event', {...}); */ fire>(name: K, args?: U, bubble?: boolean) { return this.dispatch(name, args, bubble); }, /** * Dispatches the specified event by name. Consult the * event reference for more details on each event. * * @method dispatch * @param {String} name Name of the event to dispatch. * @param {Object?} args Event arguments. * @param {Boolean?} bubble True/false if the event is to be bubbled. * @return {Object} Event args instance passed in. * @example * instance.dispatch('event', {...}); */ dispatch>(this: ObservableObject, name: K, args?: U, bubble?: boolean) { const self = this; // Prevent all events except the remove/detach event after the instance has been removed if (self.removed && name !== 'remove' && name !== 'detach') { return EventUtils.normalize(name.toLowerCase(), args ?? {} as U, self); } const dispatcherArgs = getEventDispatcher(self).dispatch(name, args); // Bubble event up to parents if (bubble !== false && self.parent) { let parent = self.parent(); while (parent && !dispatcherArgs.isPropagationStopped()) { parent.dispatch(name, dispatcherArgs, false); parent = parent.parent ? parent.parent() : undefined; } } return dispatcherArgs; }, /** * Binds an event listener to a specific event by name. Consult the * event reference for more details on each event. * * @method on * @param {String} name Event name or space separated list of events to bind. * @param {Function} callback Callback to be executed when the event occurs. * @param {Boolean} prepend Optional flag if the event should be prepended. Use this with care. * @return {Object} Current class instance. * @example * instance.on('event', (e) => { * // Callback logic * }); */ on(this: ObservableObject, name, callback, prepend?) { return getEventDispatcher(this).on(name, callback, prepend); }, /** * Unbinds an event listener to a specific event by name. Consult the * event reference for more details on each event. * * @method off * @param {String?} name Name of the event to unbind. * @param {Function?} callback Callback to unbind. * @return {Object} Current class instance. * @example * // Unbind specific callback * instance.off('event', handler); * * // Unbind all listeners by name * instance.off('event'); * * // Unbind all events * instance.off(); */ off(this: ObservableObject, name?, callback?) { return getEventDispatcher(this).off(name, callback); }, /** * Bind the event callback and once it fires the callback is removed. Consult the * event reference for more details on each event. * * @method once * @param {String} name Name of the event to bind. * @param {Function} callback Callback to bind only once. * @return {Object} Current class instance. */ once(this: ObservableObject, name, callback) { return getEventDispatcher(this).once(name, callback); }, /** * Returns true/false if the object has a event of the specified name. * * @method hasEventListeners * @param {String} name Name of the event to check for. * @return {Boolean} true/false if the event exists or not. */ hasEventListeners(this: ObservableObject, name) { return getEventDispatcher(this).has(name); } }; export default Observable;