-
Notifications
You must be signed in to change notification settings - Fork 164
Plugin lifecycle
Go back to Create your own plugin
The lifecycle of a plugin contains these stages:
- Instantiation
- Initialization
- Event handling
- Disposing
Similarly, the editor object has its lifecycle as well:
- Instantiation
- Editing (event triggering and dispatching)
- Disposing
We can see that there isn't an initialization stage for editor. After instantiation editor is ready to use, however to use a plugin there are two steps, instantiation and initialization. This is because when instantiate an editor, we need to pass in the instance of plugins, so plugins must be instantiated before editor. Then before passing into editor, plugin is not really ready to use because it is not coupled with an editor yet, so there need to be a secondary stage for plugin called initialization.
If we combine the lifecycle of editor and plugin together, we will see like this:
Initialization:
- Plugin: Instantiation
- Editor: Instantiation
- Plugin: Initialization
Working:
- Editor: Editing (event traggering and dispatching)
- Plugin: Event handling
Disposing:
- Editor: Disposing
- Plugin: Disposing
Step 1 is to call constructor of a plugin. Each plugin has their own constructor. Each plugin has its own constructor. Normally a constructor takes parameters to customize this plugin, and store them as object member. While constructor is being called, plugin doesn't have the instance of editor yet, so it can't do any initialzation work about editor. All this work need to wait for initializtion function.
Step 2 is to call constructor of editor. It will do the following jobs:
- Check the passed in HTML element is a DIV element
- Create editor core object
- Call initialize() function of each plugin (Step 2 of editor lifecycle)
- Set initial content according to options, and trigger
ContentChanged
event with source='SetContent' - Initialize Undo service
- Create DOM event handlers
- Make the passed in HTML element content editable
- Other works, such as disable some browser built-in behaviors
Step 2 need to be done before further steps, this is because all editor APIs are relying on editor core object. Without editor core object, editor core API will resut in runtime exception.
Step 3 is the step to initialize plugins. This need to be done before step 4 so that when set initial content in step 4, plugins can receive the ContentChanged event and handle it. And since this step is after step 2, it is safe for plugins to call most of the editor APIs in initialization function except undo APIs.
Step 4 sets initial content according to EditorOptions.initialContent
and apply default format according to EditorOptions.defaultFormat
in editor. Since this step is done before step 5, undo service will not receive any information about initial content, which means initial content can't be undone. If you want your initial content can be undone, use editor.setContent() instead of using EditorOptions.initialContent
.
Step 5 initial undo service. If EditorOptions.undo
is set, editor will use this object as undo service, otherwise editor will create a new instance of Undo
class as undo service. Then this object will be appended to plugin array so that it can receive further plugin events as well. When using Undo class, it will create an initial snapshot here according to current editor content.
Step 6 creates DOM event handlers for editor content DIV so that editor will receive DOM events such as onkeydown, onkeyup, ...
Step 7 adds contenteditable attribute to editor content DIV as long as EditorOptions.omitContentEditableAttributeChanges
is not set to true. Then the content DIV is becoming an editor.
User's action on editor content DIV and some editor API can trigger plugin event. Once an event is triggered, editor will dispatch it to plugins. This can be splitted into two stages:
Editor will first ask each plugin with the event object to see if a plugin wants to handle this event exclusively. This is done be calling EditorPlugin.willHandleEventExclusively()
. This is an optional method and if it is not implemented by a plugin, it means this plugin will never handle any event exclusively. If any plugin returns true from this method, editor will immediately stop asking other plugins and call onPluginEvent
of this plugin and will not call onPluginEvent() with this event object to any other plugins, so that to other plugins, it looks as if the event never happens.
If there isn't any plugin returns true from EditorPlugin.willHandleEventExclusively()
, editor will then loop each plugin to call onPluginEvent()
method with this event object, so that all plugins can handle this event.
One exception case is that when the event is triggered, it is set to 'broadcast' mode, then editor will skip the checking exclusiveness stage and go to dispatching stage directly. This can be used when some event must be broadcasted to all plugins and no one should handle it exclusively.
Note that this is the default setting of triggering event by using Editor.triggerEvent()
API and Editor.triggerContentChangedEvent()
API, but not the case when an event is triggered from browser DOM event.
public triggerEvent(pluginEvent: PluginEvent, broadcast: boolean = true);
Editor.dispose()
needs to be called outside editor. It is the responsibility of programmer who creates editor to dispose the editor as well. Editor.dispose()
works in the following stages:
- Dispose plugins (Step 7 of combined lifecycle)
- Dispose event handlers
- Dispose custom data if any
- Remove contenteditable attribute from content DIV
- Dispose editor core object
So that dispose() method of plugins are called from dispose() method of editor in step 1. Programmer doesn't need to manually call EditorPlugin.dispose()
. And since this is done before other steps, it is still ok here to call any editor APIs, but we don't recommend doing this. Because some editor API can trigger plugin event, and the event will be dispatched to other plugins, which may already be disposed, this may cause unpredictable result. In future version we may make the APIs no-op when it is called from dispose() method.
Since instantiation and initialization of plugin is separated, it is possible that of the instantiation, initialization and disposing is called multiple times for one plugin object, which means a plugin object can be reused by multiple editor sessions sequencely, as long as plugin can manage its internal data structure well.
However, NEVER reuse a plugin instance with two or more editor sessions at the same time! This may cause a lot of issues. Each editor should have its own plugin set and not share with others. Only reuse plugin if you are quite sure that when a new editor session is starting, previous editor session must already be ended.