Skip to content

Event Cache API

Jiuqing Song edited this page Feb 3, 2019 · 1 revision

When an event is fired from editor, it will be passed to all plugins, and each plugin can decide how to handle this event, or ignore it. Sometimes multiple plugins can do similar handling of one event.

For example, pressing one key may cause multiple plugins to look back from current cursor position see what are the characters before cursor. This needs to create an instance of PositionContentSearcher class. However, it will be a waste if each plugin create a new instance because the PositionContentSearcher instance can be reused.

In order to help reuse object among plugins for one event, roosterjs provides event cache API.

Get/create/clear object

cacheGetEventData is the API to get/create an object from a PluginEvent object.

function cacheGetEventData<T>(event: PluginEvent, key: string, getter: () => T): T;

event is the PluginEvent object. This API will first seek from this event object with the given key to see if we already have an instance and return it. Otherwise, the getter() function will be called to create an instance.

key is the cache key of this object, needs to be unique.

getter is a callback function. It will be called if no object instance with the given key exists in the given event object.

Once this function is called, there must be an object entry with the given key in the given event. The value will be stored in PluginEvent.eventDataCache. So next time the API is called with the same event object and key, the getter must not be called again, event if the object value is null or undefined.

To manually clear the cached object, you can use clearEventDataCache API:

function clearEventDataCache(event: PluginEvent, key: string): void

This is useful when the DOM structure is changed after some DOM operation which causes the cached object no longer be available.

Event cache wrapper

cacheGetEventData API requires a getter function as the third parameter. However, it will be very redundant to have this getter function everytime this API is called. So a better pattern is to have a wrapper function for each type of cached object, then we only need to call this wrapper function to get the object.

RoosterJs has two such wrapper functions:

PositionContentSearcher

cacheGetContentSearcher is a wrapper API for PositionContentSearcher class, and it wraps Editor.getContentSearchOfCurosr() API as the getter:

const CONTENTSEARCHER_KEY = 'CONTENTSEARCHER';

function cacheGetContentSearcher(
    event: PluginEvent,
    editor: Editor
): PositionContentSearcher {
    return cacheGetEventData(event, CONTENTSEARCHER_KEY, () => editor.getContentSearcherOfCursor());
}

With this API, each time we need to get a PositionContentSearcher from an event, we just need to call this API without providing a getter:

let searcher = cacheGetContentSearcher(event, editor);

Element at cursor

Similar, Editor.getElementAtCursor() also has a event cache wrapper: cacheGetElementAtCursor

const CACHE_KEY_PREFIX = 'GET_ELEMENT_AT_CURSOR_';

default function cacheGetElementAtCursor(
    editor: Editor,
    event: PluginEvent,
    selector: string
): HTMLElement {
    return cacheGetEventData(event, CACHE_KEY_PREFIX + selector, () =>
        editor.getElementAtCursor(selector)
    );
}