History Handling

General information

Xeditor offers an undo / redo feature that works out of the box for all user actions modifying the content. A lot of Xeditors API Methods already have build in history handling, so there's nothing additional for you to do as long as you either use:

  • Xeditor default buttons for manipulating the document, e.g. inserting elements
  • Xeditor API Methods that already include history handling. Those Methods are marked with an HISTORY tag in our API and include this part in their description: "This method includes history handling. A new entry is prepared before and committed after the action was successful."

If you are using methods that do not contain history handling, e.g. like insertElement, the history handling needs to be implemented on your side. The reason behind this is that Xeditor can't know if you want to perform multiple actions at once (e.g. inserting multiple elements), and the user probably doesn't want to use the undo function multiple times even though only one action was performed.

All actions related to history handling are performed by the History Manager class. All related methods and properties are described in the linked API documentation of this class. However, the following will cover the most important actions and include some examples to get you started.

How to use it

Even though the History Manager offers multiple API methods, the most important ones for the most common actions are

As the names might already suggest, prepare is used to prepare a history step, while commit is used to indicate the end of a history step. Meaning all actions that are performed in between those two calls will be part of one history step, and can therefore be addressed with one undo / redo action.

In general, all code interacting with history handling will look something like this:

// prepare history entry
// note: this has to be called **before** any actions that should be part of a
// history step are performed
editor.getHistoryManager().prepare();
// any custom logic that inserts elements, changes content,
// or basically performs any action that should be part of a
// history entry
// commit history entry and thus making the whole action undoable
editor.getHistoryManager().commit();

Canceling a history step

In some cases it might be required to cancel an already prepared history entry. This might be the case if your code is already performing some actions and for some reason some parts fail and hence, no action was performed. Since we do not want to create an empty history entry, as this would result in the user using the undo functionality without anything happening, we want to cancel the history entry instead. For this, the History Manager offers the method rollback. By this call, all actions that were performed after the call of historyManager.prepare are reverted and the history entry will be cancelled. So, our quick example from above would look something like this:

// prepare history entry
// note: this has to be called **before** any actions that should be part of a
// history step are performed
editor.getHistoryManager().prepare();
// any custom logic that inserts elements, changes content,
// or basically performs any action that should be part of a
// history entry
// something broke or didn't work properly
if (myFailedCondition) { // whatever might indicate that the action was unsuccessful
editor.getHistoryManager().rollback(); // revert history
return; // stop execution
}
// commit history entry and thus making the whole action undoable
editor.getHistoryManager().commit();

Examples

Insert multiple elements

This snippet will insert two paragraphs after the current one. The user will be able to undo / redo the entire action with one trigger of the undo / redo action.

var selectionElement = editor.getSelectionManager().getElement();
if (!selectionElement) {
return;
}
// get selected paragraph
var selectedPara = selectionElement.findParentByTypes(true, ['paragraph']);
if (!selectedPara) {
return;
}
// create elements to be inserted
var para1 = editor.getDocument().createElement('paragraph');
var para2 = editor.getDocument().createElement('paragraph');
// prepare history entry
editor.getHistoryManager().prepare();
// insert both paragraphs after selected one
selectedPara.getParent().insertBefore(para1, selectedPara.findNextNodeSibling());
selectedPara.getParent().insertBefore(para2, para1.findNextNodeSibling());
// commit history entry
editor.getHistoryManager().commit();

Cancel created history entry

In some cases it might occur that a performed action wasn't succesful. If a history entry was already prepared, commiting an empty history entry, would result in the undo action doing nothing, which would be confusing to the user. Therefore, if for some reason our desired action couldn't be performed, we want to cancel created history entry. The following will give you an example on how to do this.

In this example we will insert a break until the selected paragraph element. If the action fails for whatever reason, we will revert the action and cancel the created history entry:

var selectionElement = editor.getSelectionManager().getElement();
if (!selectionElement) {
return;
}
// get selected paragraph
var selectedPara = selectionElement.findParentByTypes(true, ['paragraph']);
if (!selectedPara) {
return;
}
// prepare history
editor.getHistoryManager().prepare();
// insert break
var breakResult = editor.getDocument().breakUntil(selectedPara);
if (!breakResult) { // break did not work
// rollback history entry
editor.getHistoryManager().rollback();
return;
}
// commit history
editor.getHistoryManager().commit();